Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #132 restore ACL/SCM validation error responses #133

Merged
merged 1 commit into from
Nov 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions src/main/java/org/rundeck/client/tool/commands/AppCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.rundeck.client.util.Client;
import org.rundeck.client.util.ServiceClient;
import retrofit2.Call;
import retrofit2.Response;

import java.io.IOException;
import java.util.function.Function;
Expand Down Expand Up @@ -63,18 +62,18 @@ public <T> T apiCall(Function<RundeckApi, Call<T>> func) throws InputError, IOEx
}

/**
* Perform a downgradable API call
* Perform a downgradable API call without handling errors in response
*
* @param func function
* @param <T> result type
*
* @return result
* @return response
*
* @throws InputError on error
* @throws IOException on error
*/
public <T> Response<T> apiResponse(Function<RundeckApi, Call<T>> func) throws InputError, IOException {
return apiResponseDowngradable(rdApp, func);
public <T> ServiceClient.WithErrorResponse<T> apiWithErrorResponse(Function<RundeckApi, Call<T>> func) throws InputError, IOException {
return apiWithErrorResponseDowngradable(rdApp, func);
}

/**
Expand Down Expand Up @@ -127,32 +126,32 @@ public static <T> T apiCallDowngradable(
}
}
/**
* Perform a downgradable api call
* Perform a downgradable api call, without handling errors
*
* @param rdApp app
* @param func function
* @param <T> result type
*
* @return result
* @return response
*
* @throws InputError on error
* @throws IOException on error
*/
public static <T> Response<T> apiResponseDowngradable(
public static <T> ServiceClient.WithErrorResponse<T> apiWithErrorResponseDowngradable(
final RdApp rdApp,
final Function<RundeckApi, Call<T>> func
)
throws InputError, IOException
{
try {
return rdApp.getClient().apiResponseDowngradable(func);
return rdApp.getClient().apiWithErrorResponseDowngradable(func);
} catch (Client.UnsupportedVersionDowngrade downgrade) {
//downgrade to supported version and try again
rdApp.versionDowngradeWarning(
downgrade.getRequestedVersion(),
downgrade.getSupportedVersion()
);
return rdApp.getClient(downgrade.getSupportedVersion()).apiResponse(func);
return rdApp.getClient(downgrade.getSupportedVersion()).apiWithErrorResponse(func);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/rundeck/client/tool/commands/Jobs.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ public void list(ListOpts options, CommandOutput output) throws IOException, Inp
));
}
if ((!"yaml".equals(options.getFormat()) ||
!ServiceClient.hasAnyMediaType(body, Client.MEDIA_TYPE_YAML, Client.MEDIA_TYPE_TEXT_YAML)) &&
!ServiceClient.hasAnyMediaType(body, Client.MEDIA_TYPE_XML, Client.MEDIA_TYPE_TEXT_XML)) {
!ServiceClient.hasAnyMediaType(body.contentType(), Client.MEDIA_TYPE_YAML, Client.MEDIA_TYPE_TEXT_YAML)) &&
!ServiceClient.hasAnyMediaType(body.contentType(), Client.MEDIA_TYPE_XML, Client.MEDIA_TYPE_TEXT_XML)) {

throw new IllegalStateException("Unexpected response format: " + body.contentType());
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/rundeck/client/tool/commands/Keys.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public boolean get(GetOpts options, CommandOutput output) throws IOException, In
return false;
}
ResponseBody body = apiCall(api -> api.getPublicKey(path.keysPath()));
if (!ServiceClient.hasAnyMediaType(body, Client.MEDIA_TYPE_GPG_KEYS)) {
if (!ServiceClient.hasAnyMediaType(body.contentType(), Client.MEDIA_TYPE_GPG_KEYS)) {
throw new IllegalStateException("Unexpected response format: " + body.contentType());
}
InputStream inputStream = body.byteStream();
Expand Down
26 changes: 18 additions & 8 deletions src/main/java/org/rundeck/client/tool/commands/projects/ACLs.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,31 +176,41 @@ public static ACLPolicy performACLModify(
Client.MEDIA_TYPE_YAML,
input
);
Response<ACLPolicy> execute = apiResponseDowngradable(rdApp, (RundeckApi api) -> func.apply(requestBody, api));
checkValidationError(output, rdApp.getClient(), execute, input.getAbsolutePath());
ServiceClient.WithErrorResponse<ACLPolicy> execute = apiWithErrorResponseDowngradable(
rdApp,
api -> func.apply(requestBody, api)
);
checkValidationError(
output,
rdApp.getClient(),
execute,
input.getAbsolutePath(),
rdApp.getAppConfig().isAnsiEnabled()
);
return rdApp.getClient().checkError(execute);
}

private static void checkValidationError(
CommandOutput output,
final ServiceClient<RundeckApi> client,
final Response<ACLPolicy> response, final String filename
final ServiceClient.WithErrorResponse<ACLPolicy> errorResponse,
final String filename, final boolean colorize
)
throws IOException
{

if (!response.isSuccessful() && response.code() == 400) {
Response<ACLPolicy> response = errorResponse.getResponse();
if (errorResponse.isError400()) {
ACLPolicyValidation error = client.readError(
response,
errorResponse.getErrorBody(),
ACLPolicyValidation.class,
Client.MEDIA_TYPE_JSON
);
if (null != error) {
Optional<Map<String, Object>> validationData = Optional.ofNullable(error.toMap());
validationData.ifPresent(map -> {
output.error("ACL Policy Validation failed for the file: ");
output.output(filename + "\n");
output.output(Colorz.colorizeMapRecurse(map, ANSIColorOutput.Color.YELLOW));
output.output(filename);
output.output(colorize ? Colorz.colorizeMapRecurse(map, ANSIColorOutput.Color.YELLOW) : map);
});
if (!validationData.isPresent() && "true".equals(error.error)) {
output.error("Invalid Request:");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private static void receiveArchiveFile(
)
throws IOException
{
if (!ServiceClient.hasAnyMediaType(responseBody, Client.MEDIA_TYPE_ZIP)) {
if (!ServiceClient.hasAnyMediaType(responseBody.contentType(), Client.MEDIA_TYPE_ZIP)) {
throw new IllegalStateException("Unexpected response format: " + responseBody.contentType());
}
InputStream inputStream = responseBody.byteStream();
Expand Down
85 changes: 50 additions & 35 deletions src/main/java/org/rundeck/client/tool/commands/projects/SCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public boolean setup(SetupOptions options, CommandOutput output) throws IOExcept
String project = projectOrEnv(options);

//get response to handle 400 validation error
Response<ScmActionResult> response = apiResponse(api -> api
ServiceClient.WithErrorResponse<ScmActionResult> response = apiWithErrorResponse(api -> api
.setupScmConfig(
project,
options.getIntegration(),
Expand All @@ -124,8 +124,12 @@ public boolean setup(SetupOptions options, CommandOutput output) throws IOExcept
));

//check for 400 error with validation information
if (hasValidationError(output, getClient(), response,
"Setup config Validation for file: " + options.getFile().getAbsolutePath()
if (hasValidationError(
output,
getClient(),
response,
"Setup config Validation for file: " + options.getFile().getAbsolutePath(),
getAppConfig().isAnsiEnabled()
)) {
return false;
}
Expand Down Expand Up @@ -289,16 +293,20 @@ public boolean perform(ActionPerformOptions options, CommandOutput output) throw

ScmActionPerform perform = performFromOptions(options);
String project = projectOrEnv(options);
Response<ScmActionResult> response = apiResponse(api -> api.performScmAction(
ServiceClient.WithErrorResponse<ScmActionResult> response = apiWithErrorResponse(api -> api.performScmAction(
project,
options.getIntegration(),
options.getAction(),
perform
));

//check for 400 error with validation information
if (hasValidationError(output, getClient(), response,
"Action " + options.getAction()
if (hasValidationError(
output,
getClient(),
response,
"Action " + options.getAction(),
getAppConfig().isAnsiEnabled()
)) {
return false;
}
Expand Down Expand Up @@ -340,44 +348,51 @@ public void plugins(ListPluginsOptions options, CommandOutput output) throws IOE
* Check for validation info from resposne
*
* @param name action name for error messages
* @param colorize
*/
private static boolean hasValidationError(
CommandOutput output,
final ServiceClient<RundeckApi> serviceClient,
final Response<ScmActionResult> response,
final String name
final ServiceClient.WithErrorResponse<ScmActionResult> errorResponse,
final String name, final boolean colorize
)
{
if (!response.isSuccessful()) {
if (response.code() == 400) {
try {
//parse body as ScmActionResult
ScmActionResult error = serviceClient.readError(
response,
ScmActionResult.class,
Client.MEDIA_TYPE_JSON
);
if (null != error) {
//
output.error(String.format("%s failed", name));
if (null != error.message) {
output.warning(error.message);
}
Optional<? extends Map<?, ?>> errorData = Optional.ofNullable(error.toMap());
errorData.ifPresent(map -> output.output(Colorz.colorizeMapRecurse(map, ANSIColorOutput.Color.YELLOW)));
Response<ScmActionResult> response = errorResponse.getResponse();
if (errorResponse.isError400()) {
try {
//parse body as ScmActionResult
ScmActionResult error = serviceClient.readError(
errorResponse.getErrorBody(),
ScmActionResult.class,
Client.MEDIA_TYPE_JSON
);
if (null != error) {
//
output.error(String.format("%s failed", name));
if (null != error.message) {
output.warning(error.message);
}
} catch (IOException e) {
//unable to parse body as expected
throw new RequestFailed(String.format(
"%s failed: (error: %d %s)",
name,
response.code(),
response.message()

), response.code(), response.message());
Optional<? extends Map<?, ?>> errorData = Optional.ofNullable(error.toMap());
errorData.ifPresent(map -> output.output(
colorize ?
Colorz.colorizeMapRecurse(
map,
ANSIColorOutput.Color.YELLOW
) : map
));
}

} catch (IOException e) {
//unable to parse body as expected
e.printStackTrace();
throw new RequestFailed(String.format(
"%s failed: (error: %d %s)",
name,
response.code(),
response.message()

), response.code(), response.message());
}

}
return !response.isSuccessful();
}
Expand Down
Loading