GUACAMOLE-926: Always use translation system for errors.

This commit is contained in:
James Muehlner
2023-04-06 00:39:19 +00:00
parent d657d2b90a
commit 8aaa636705
6 changed files with 113 additions and 26 deletions

View File

@@ -155,6 +155,10 @@ angular.module('import').directive('connectionImportErrors', [
if (!patchFailure || !parseResult) if (!patchFailure || !parseResult)
return; return;
// All promises from all translation requests. The scope will not be
// updated until all translations are ready.
const translationPromises = [];
// Set up the list of connection errors based on the existing parse // Set up the list of connection errors based on the existing parse
// result, with error messages fetched from the patch failure // result, with error messages fetched from the patch failure
$scope.connectionErrors = parseResult.patches.map( $scope.connectionErrors = parseResult.patches.map(
@@ -166,10 +170,24 @@ angular.module('import').directive('connectionImportErrors', [
// Set the error from the PATCH request, if there is one // Set the error from the PATCH request, if there is one
const error = _.get(patchFailure, ['patches', index, 'error']); const error = _.get(patchFailure, ['patches', index, 'error']);
if (error) if (error)
connectionError.errors = new DisplayErrorList([error]);
// Fetch the translation and update it when it's ready
translationPromises.push($translate(
error.key, error.variables)
.then(translatedError =>
connectionError.errors.getArray().push(translatedError)
));
return connectionError; return connectionError;
}); });
// Once all the translations have been completed, update the
// connectionErrors all in one go, to ensure no excessive reloading
$q.all(translationPromises).then(() => {
$scope.connectionErrors = connectionErrors;
});
}); });
// If a new parse result with errors is seen, update the display list // If a new parse result with errors is seen, update the display list

View File

@@ -61,6 +61,13 @@ angular.module('import').factory('ParseError', [function defineParseError() {
*/ */
this.variables = template.variables; this.variables = template.variables;
// If no translation key is available, fall back to the untranslated
// key, passing the raw message directly through the translation system
if (!this.key) {
this.key = 'APP.TEXT_UNTRANSLATED';
this.variables = { MESSAGE: this.message };
}
}; };
return ParseError; return ParseError;

View File

@@ -68,7 +68,7 @@ angular.module('rest').factory('DirectoryPatchOutcome', [
* The error message associated with the failure, if the patch failed to * The error message associated with the failure, if the patch failed to
* apply. * apply.
* *
* @type {String} * @type {TranslatableMessage}
*/ */
this.error = template.error; this.error = template.error;

View File

@@ -21,10 +21,13 @@ package org.apache.guacamole.rest.directory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@@ -39,6 +42,8 @@ import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.language.Translatable;
import org.apache.guacamole.language.TranslatableMessage;
import org.apache.guacamole.net.auth.AtomicDirectoryOperation; import org.apache.guacamole.net.auth.AtomicDirectoryOperation;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -53,6 +58,7 @@ import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.net.event.DirectoryEvent; import org.apache.guacamole.net.event.DirectoryEvent;
import org.apache.guacamole.net.event.DirectoryFailureEvent; import org.apache.guacamole.net.event.DirectoryFailureEvent;
import org.apache.guacamole.net.event.DirectorySuccessEvent; import org.apache.guacamole.net.event.DirectorySuccessEvent;
import org.apache.guacamole.rest.APIError;
import org.apache.guacamole.rest.event.ListenerService; import org.apache.guacamole.rest.event.ListenerService;
import org.apache.guacamole.rest.jsonpatch.APIPatch; import org.apache.guacamole.rest.jsonpatch.APIPatch;
import org.apache.guacamole.rest.jsonpatch.APIPatchError; import org.apache.guacamole.rest.jsonpatch.APIPatchError;
@@ -415,6 +421,61 @@ public abstract class DirectoryResource<InternalType extends Identifiable, Exter
} }
/**
* If the provided throwable is a known Guacamole-specific type, create and
* return a APIPatchError with an error message extracted from the error.
* If the provided throwable is not a known type, null will be returned.
*
* @param op
* The operation being attempted when the error occurred.
*
* @param identifier
* The identifier of the object in question, if any.
*
* @param path
* The path for the patch that was being applied when the error occurred.
*
* @param t
* The error that occurred while attempting to apply the patch.
*
* @return
* A APIPatchError with an error message extracted from the provided
* throwable - if it's a known type, otherwise null.
*/
@Nullable
private APIPatchError createPatchFailure(
@Nonnull APIPatch.Operation op, @Nullable String identifier,
@Nonnull String path, @Nonnull Throwable t) {
/*
* If the failure is a translatable type, use the translation directly
* in the patch error.
*/
if (t instanceof Translatable)
return new APIPatchError(
op, identifier, path,
((Translatable) t).getTranslatableMessage());
/*
* If the failure represents a known Guacamole exception but is not
* translateable, create a patch error containing the raw untranslated
* exception message.
*/
if (t instanceof GuacamoleException) {
// Create a translated message that will fall
// through to the untranslated message
TranslatableMessage message = new TranslatableMessage(
"APP.TEXT_UNTRANSLATED", Collections.singletonMap(
"MESSAGE", ((GuacamoleException) t).getMessage()));
return new APIPatchError(op, identifier, path, message);
}
// The error is not a known type - no patch error can be generated
return null;
}
/** /**
* Applies the given object patches, updating the underlying directory * Applies the given object patches, updating the underlying directory
* accordingly. This operation supports addition and removal of objects * accordingly. This operation supports addition and removal of objects
@@ -512,19 +573,19 @@ public abstract class DirectoryResource<InternalType extends Identifiable, Exter
DirectoryEvent.Operation.ADD, DirectoryEvent.Operation.ADD,
internal.getIdentifier(), internal, e); internal.getIdentifier(), internal, e);
/* // Attempt to generate an API Patch error using the
* If the failure represents an understood issue, // caught exception
* create a failure outcome for this failed patch. APIPatchError patchError = createPatchFailure(
*/ op, null, path, e);
if (e instanceof GuacamoleException)
patchOutcomes.add(new APIPatchError(
op, null, path,
((GuacamoleException) e).getMessage()));
// If an unexpected failure occurs, fall through to the if (patchError != null)
// standard API error handling patchOutcomes.add(patchError);
// If an unexpected failure occurs, fall through to
// the standard API error handling
else else
throw e; throw e;
} }
} }
@@ -554,17 +615,16 @@ public abstract class DirectoryResource<InternalType extends Identifiable, Exter
DirectoryEvent.Operation.REMOVE, DirectoryEvent.Operation.REMOVE,
identifier, null, e); identifier, null, e);
/* // Attempt to generate an API Patch error using the
* If the failure represents an understood issue, // caught exception
* create a failure outcome for this failed patch. APIPatchError patchError = createPatchFailure(
*/ op, identifier, path, e);
if (e instanceof GuacamoleException)
patchOutcomes.add(new APIPatchError(
op, identifier, path,
((GuacamoleException) e).getMessage()));
// If an unexpected failure occurs, fall through to the if (patchError != null)
// standard API error handling patchOutcomes.add(patchError);
// If an unexpected failure occurs, fall through to
// the standard API error handling
else else
throw e; throw e;
} }

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.rest.jsonpatch; package org.apache.guacamole.rest.jsonpatch;
import org.apache.guacamole.language.TranslatableMessage;
import org.apache.guacamole.rest.jsonpatch.APIPatch.Operation; import org.apache.guacamole.rest.jsonpatch.APIPatch.Operation;
/** /**
@@ -33,7 +34,7 @@ public class APIPatchError extends APIPatchOutcome {
/** /**
* The error associated with the submitted patch. * The error associated with the submitted patch.
*/ */
private final String error; private final TranslatableMessage error;
/** /**
* Create a failure status associated with a submitted patch from a JSON * Create a failure status associated with a submitted patch from a JSON
@@ -54,7 +55,8 @@ public class APIPatchError extends APIPatchOutcome {
* patch from applying. * patch from applying.
*/ */
public APIPatchError( public APIPatchError(
Operation op, String identifier, String path, String error) { Operation op, String identifier, String path,
TranslatableMessage error) {
super(op, identifier, path); super(op, identifier, path);
this.error = error; this.error = error;
} }
@@ -65,7 +67,7 @@ public class APIPatchError extends APIPatchOutcome {
* @return * @return
* The error associated with the patch failure. * The error associated with the patch failure.
*/ */
public String getError() { public TranslatableMessage getError() {
return error; return error;
} }
} }

View File

@@ -51,7 +51,7 @@ public class APIPatchFailureException extends GuacamoleClientException {
public APIPatchFailureException( public APIPatchFailureException(
String message, List<APIPatchOutcome> patches) { String message, List<APIPatchOutcome> patches) {
super(message ); super(message);
this.patches = patches; this.patches = patches;
} }