SEBSERV-135 improved identity handling and error messages

This commit is contained in:
anhefti 2021-06-28 17:33:24 +02:00
parent 730ec7dfc8
commit d76f1f5ca9
16 changed files with 77 additions and 52 deletions

View file

@ -203,7 +203,7 @@ public class APIMessage implements Serializable {
return ErrorMessage.FIELD_VALIDATION.of(fieldName, args); return ErrorMessage.FIELD_VALIDATION.of(fieldName, args);
} }
public static String toHTML(final String errorMessage, final List<APIMessage> messages) { public static String toHTML(final String errorMessage, final Collection<APIMessage> messages) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
builder.append("<b>Failure:</b>").append("<br/><br/>").append(errorMessage).append("<br/><br/>"); builder.append("<b>Failure:</b>").append("<br/><br/>").append(errorMessage).append("<br/><br/>");
builder.append("<b>Detail Messages:</b><br/><br/>"); builder.append("<b>Detail Messages:</b><br/><br/>");
@ -220,7 +220,7 @@ public class APIMessage implements Serializable {
* within an Exception and throw. The Exception will be caught a the * within an Exception and throw. The Exception will be caught a the
* APIExceptionHandler endpoint. The APIMessage will be extracted * APIExceptionHandler endpoint. The APIMessage will be extracted
* and send as response. */ * and send as response. */
public static class APIMessageException extends RuntimeException { public static class APIMessageException extends RuntimeException implements APIMessageError {
private static final long serialVersionUID = 1453431210820677296L; private static final long serialVersionUID = 1453431210820677296L;
@ -246,6 +246,7 @@ public class APIMessage implements Serializable {
this.apiMessages = Arrays.asList(errorMessage.of(detail, attributes)); this.apiMessages = Arrays.asList(errorMessage.of(detail, attributes));
} }
@Override
public Collection<APIMessage> getAPIMessages() { public Collection<APIMessage> getAPIMessages() {
return this.apiMessages; return this.apiMessages;
} }
@ -283,11 +284,11 @@ public class APIMessage implements Serializable {
} }
public static boolean checkError(final Exception error, final ErrorMessage errorMessage) { public static boolean checkError(final Exception error, final ErrorMessage errorMessage) {
if (!(error instanceof APIMessageException)) { if (!(error instanceof APIMessageError)) {
return false; return false;
} }
final APIMessageException _error = (APIMessageException) error; final APIMessageError _error = (APIMessageError) error;
return _error.getAPIMessages() return _error.getAPIMessages()
.stream() .stream()
.filter(msg -> errorMessage.messageCode.equals(msg.messageCode)) .filter(msg -> errorMessage.messageCode.equals(msg.messageCode))

View file

@ -8,14 +8,14 @@
package ch.ethz.seb.sebserver.gbl.api; package ch.ethz.seb.sebserver.gbl.api;
import java.util.List; import java.util.Collection;
/** Defines an API message error holder that supplies a List of APIMessage if error happened */ /** Defines an API message error holder that supplies a List of APIMessage if error happened */
public interface APIMessageError { public interface APIMessageError {
/** Get a List of APIMessage errors if error happened /** Get a List of APIMessage errors if error happened
* *
* @return a List of APIMessage errors if error happened or empty list of not*/ * @return a List of APIMessage errors if error happened or empty list of not */
List<APIMessage> getErrorMessages(); Collection<APIMessage> getAPIMessages();
} }

View file

@ -203,10 +203,16 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
this.configPurpose = configPurpose; this.configPurpose = configPurpose;
this.sebServerPingTime = sebServerPingTime; this.sebServerPingTime = sebServerPingTime;
this.vdiType = vdiType; this.vdiType = vdiType != null ? vdiType : VDIType.NO;
this.vdiExecutable = vdiExecutable != null ? vdiExecutable : vdiType.defaultExecutable; this.vdiExecutable = vdiExecutable != null
this.vdiPath = vdiPath != null ? vdiPath : vdiType.defaultPath; ? vdiExecutable
this.vdiArguments = vdiArguments != null ? vdiArguments : vdiType.defaultArguments; : vdiType != null ? vdiType.defaultExecutable : null;
this.vdiPath = vdiPath != null
? vdiPath
: vdiType != null ? vdiType.defaultPath : null;
this.vdiArguments = vdiArguments != null
? vdiArguments
: vdiType != null ? vdiType.defaultArguments : null;
this.fallback = fallback; this.fallback = fallback;
this.fallbackStartURL = fallbackStartURL; this.fallbackStartURL = fallbackStartURL;

View file

@ -176,7 +176,7 @@ public class CertificateImportPopup {
final Exception error = result.getError(); final Exception error = result.getError();
if (error instanceof RestCallError) { if (error instanceof RestCallError) {
((RestCallError) error) ((RestCallError) error)
.getErrorMessages() .getAPIMessages()
.stream() .stream()
.findFirst() .findFirst()
.ifPresent(message -> { .ifPresent(message -> {

View file

@ -75,6 +75,8 @@ public class CertificateList implements TemplateComposer {
new LocTextKey("sebserver.certificate.action.import-config.confirm"); new LocTextKey("sebserver.certificate.action.import-config.confirm");
static final LocTextKey FORM_ACTION_MESSAGE_IN_USE_TEXT_KEY = static final LocTextKey FORM_ACTION_MESSAGE_IN_USE_TEXT_KEY =
new LocTextKey("sebserver.certificate.action.remove.in-use"); new LocTextKey("sebserver.certificate.action.remove.in-use");
static final LocTextKey FORM_ACTION_MESSAGE_REMOVE_CONFIRM_TEXT_KEY =
new LocTextKey("sebserver.certificate.action.remove.confirm");
private final TableFilterAttribute aliasFilter = new TableFilterAttribute( private final TableFilterAttribute aliasFilter = new TableFilterAttribute(
CriteriaType.TEXT, CriteriaType.TEXT,
@ -155,6 +157,7 @@ public class CertificateList implements TemplateComposer {
.publishIf(() -> grantCheck.iw()) .publishIf(() -> grantCheck.iw())
.newAction(ActionDefinition.SEB_CERTIFICATE_REMOVE) .newAction(ActionDefinition.SEB_CERTIFICATE_REMOVE)
.withConfirm(() -> FORM_ACTION_MESSAGE_REMOVE_CONFIRM_TEXT_KEY)
.withSelect( .withSelect(
table::getSelection, table::getSelection,
this::removeCertificate, this::removeCertificate,

View file

@ -16,6 +16,7 @@ import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.UrlLauncher; import org.eclipse.rap.rwt.client.service.UrlLauncher;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Listener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -332,14 +333,16 @@ public class SEBClientConfigForm implements TemplateComposer {
// VDI // VDI
.withDefaultSpanInput(2) .withDefaultSpanInput(2)
.addField(FormBuilder.singleSelection( .addFieldIf(
SEBClientConfig.ATTR_VDI_TYPE, () -> false, // TODO skipped for version 1.2 --> 1.3 or 1.4
VDI_TYPE_TEXT_KEY, () -> FormBuilder.singleSelection(
clientConfig.vdiType != null SEBClientConfig.ATTR_VDI_TYPE,
? clientConfig.vdiType.name() VDI_TYPE_TEXT_KEY,
: SEBClientConfig.VDIType.NO.name(), clientConfig.vdiType != null
() -> this.pageService.getResourceService().vdiTypeResources()) ? clientConfig.vdiType.name()
.mandatory(!isReadonly)) : SEBClientConfig.VDIType.NO.name(),
() -> this.pageService.getResourceService().vdiTypeResources())
.mandatory(!isReadonly))
.withDefaultSpanEmptyCell(3); .withDefaultSpanEmptyCell(3);
// VDI Attributes // VDI Attributes
@ -472,10 +475,18 @@ public class SEBClientConfigForm implements TemplateComposer {
}; };
if (!isReadonly) { if (!isReadonly) {
formHandleAnchor.formHandle.getForm().getFieldInput(SEBClientConfig.ATTR_FALLBACK) final Control fallbackInput = formHandleAnchor.formHandle
.addListener(SWT.Selection, selectionListener); .getForm()
formHandleAnchor.formHandle.getForm().getFieldInput(SEBClientConfig.ATTR_VDI_TYPE) .getFieldInput(SEBClientConfig.ATTR_FALLBACK);
.addListener(SWT.Selection, selectionListener); if (fallbackInput != null) {
fallbackInput.addListener(SWT.Selection, selectionListener);
}
final Control vdiInput = formHandleAnchor.formHandle
.getForm()
.getFieldInput(SEBClientConfig.ATTR_VDI_TYPE);
if (vdiInput != null) {
vdiInput.addListener(SWT.Selection, selectionListener);
}
} }
formContent.layout(); formContent.layout();

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -210,7 +211,7 @@ public class SEBExamConfigImportPopup {
final Exception error = configuration.getError(); final Exception error = configuration.getError();
if (error instanceof RestCallError) { if (error instanceof RestCallError) {
((RestCallError) error) ((RestCallError) error)
.getErrorMessages() .getAPIMessages()
.stream() .stream()
.findFirst() .findFirst()
.ifPresent(message -> { .ifPresent(message -> {
@ -393,8 +394,8 @@ public class SEBExamConfigImportPopup {
private static void notifyErrorOnSave(final Exception error, final PageContext context) { private static void notifyErrorOnSave(final Exception error, final PageContext context) {
if (error instanceof APIMessageError) { if (error instanceof APIMessageError) {
try { try {
final List<APIMessage> errorMessages = ((APIMessageError) error).getErrorMessages(); final Collection<APIMessage> errorMessages = ((APIMessageError) error).getAPIMessages();
final APIMessage apiMessage = errorMessages.get(0); final APIMessage apiMessage = errorMessages.iterator().next();
if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) { if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) {
context.publishPageMessage(new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION)); context.publishPageMessage(new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION));
} else { } else {

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@ -324,8 +325,8 @@ public class SEBSettingsForm implements TemplateComposer {
public void notifyErrorOnSave(final Exception error, final PageContext context) { public void notifyErrorOnSave(final Exception error, final PageContext context) {
if (error instanceof APIMessageError) { if (error instanceof APIMessageError) {
try { try {
final List<APIMessage> errorMessages = ((APIMessageError) error).getErrorMessages(); final Collection<APIMessage> errorMessages = ((APIMessageError) error).getAPIMessages();
final APIMessage apiMessage = errorMessages.get(0); final APIMessage apiMessage = errorMessages.iterator().next();
if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) { if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) {
throw new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION); throw new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION);
} else { } else {

View file

@ -144,13 +144,13 @@ public class FormHandle<T extends Entity> {
if (error instanceof RestCallError) { if (error instanceof RestCallError) {
final List<APIMessage> fieldValidationErrors = ((RestCallError) error) final List<APIMessage> fieldValidationErrors = ((RestCallError) error)
.getErrorMessages() .getAPIMessages()
.stream() .stream()
.filter(APIMessage.ErrorMessage.FIELD_VALIDATION::isOf) .filter(APIMessage.ErrorMessage.FIELD_VALIDATION::isOf)
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<APIMessage> noneFieldValidationErrors = ((RestCallError) error) final List<APIMessage> noneFieldValidationErrors = ((RestCallError) error)
.getErrorMessages() .getAPIMessages()
.stream() .stream()
.filter(message -> !APIMessage.ErrorMessage.FIELD_VALIDATION.isOf(message)) .filter(message -> !APIMessage.ErrorMessage.FIELD_VALIDATION.isOf(message))
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -390,7 +390,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
private String verifyErrorMessage(final Exception error) { private String verifyErrorMessage(final Exception error) {
if (error instanceof RestCallError) { if (error instanceof RestCallError) {
final List<APIMessage> errorMessages = ((RestCallError) error).getErrorMessages(); final List<APIMessage> errorMessages = ((RestCallError) error).getAPIMessages();
if (errorMessages.isEmpty()) { if (errorMessages.isEmpty()) {
return ""; return "";
} }

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.page.impl; package ch.ethz.seb.sebserver.gui.service.page.impl;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -332,7 +333,7 @@ public class PageContextImpl implements PageContext {
: error.getMessage(); : error.getMessage();
if (error instanceof APIMessageError) { if (error instanceof APIMessageError) {
final List<APIMessage> errorMessages = ((APIMessageError) error).getErrorMessages(); final Collection<APIMessage> errorMessages = ((APIMessageError) error).getAPIMessages();
final MessageBox messageBox = new Message( final MessageBox messageBox = new Message(
getShell(), getShell(),
this.i18nSupport.getText("sebserver.error.unexpected"), this.i18nSupport.getText("sebserver.error.unexpected"),

View file

@ -38,7 +38,7 @@ public class RestCallError extends RuntimeException implements APIMessageError {
} }
@Override @Override
public List<APIMessage> getErrorMessages() { public List<APIMessage> getAPIMessages() {
return this.errors; return this.errors;
} }

View file

@ -223,12 +223,7 @@ public class CertificateDAOImpl implements CertificateDAO {
// dataEncipherment // dataEncipherment
if (keyUsage[2] || keyUsage[3]) { if (keyUsage[2] || keyUsage[3]) {
final String alias = certificates.keyStore.engineGetCertificateAlias(cert); result.add(CertificateType.DATA_ENCIPHERMENT);
if (this.cryptor.getPrivateKey(certificates.keyStore, alias).hasValue()) {
result.add(CertificateType.DATA_ENCIPHERMENT_PRIVATE_KEY);
} else {
result.add(CertificateType.DATA_ENCIPHERMENT);
}
} }
// keyCertSign // keyCertSign
@ -240,6 +235,11 @@ public class CertificateDAOImpl implements CertificateDAO {
result.add(CertificateType.UNKNOWN); result.add(CertificateType.UNKNOWN);
} }
final String alias = certificates.keyStore.engineGetCertificateAlias(cert);
if (this.cryptor.getPrivateKey(certificates.keyStore, alias).hasValue()) {
result.add(CertificateType.DATA_ENCIPHERMENT_PRIVATE_KEY);
}
return result; return result;
} }

View file

@ -85,7 +85,7 @@ public abstract class AbstractCertificateCryptor {
final String algorithm = cert.getPublicKey().getAlgorithm(); final String algorithm = cert.getPublicKey().getAlgorithm();
final Cipher encryptCipher = Cipher.getInstance(algorithm); final Cipher encryptCipher = Cipher.getInstance(algorithm);
encryptCipher.init(Cipher.ENCRYPT_MODE, cert); encryptCipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
return encryptCipher.doFinal(data, 0, length); return encryptCipher.doFinal(data, 0, length);
} }
@ -99,7 +99,7 @@ public abstract class AbstractCertificateCryptor {
final String algorithm = cert.getPublicKey().getAlgorithm(); final String algorithm = cert.getPublicKey().getAlgorithm();
final Cipher encryptCipher = Cipher.getInstance(algorithm); final Cipher encryptCipher = Cipher.getInstance(algorithm);
encryptCipher.init(Cipher.DECRYPT_MODE, cert); encryptCipher.init(Cipher.DECRYPT_MODE, cert.getPublicKey());
return encryptCipher.doFinal(encryptedData, 0, length); return encryptCipher.doFinal(encryptedData, 0, length);
} }

View file

@ -1607,6 +1607,7 @@ sebserver.certificate.action.import.missing-password=The certificate file needs
sebserver.certificate.action.import-config.confirm=Certificate(s) successfully imported sebserver.certificate.action.import-config.confirm=Certificate(s) successfully imported
sebserver.certificate.message.error.file=Unsupported file type sebserver.certificate.message.error.file=Unsupported file type
sebserver.certificate.action.import-file-select.no=Please select a valid file sebserver.certificate.action.import-file-select.no=Please select a valid file
sebserver.certificate.action.remove.confirm=Are you sure you want delete the selected Certificate(s)?
sebserver.certificate.action.remove.in-use=This certificate is in use and cannot be removed. sebserver.certificate.action.remove.in-use=This certificate is in use and cannot be removed.

View file

@ -381,7 +381,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
RestCallError error = (RestCallError) call.getError(); RestCallError error = (RestCallError) call.getError();
assertEquals( assertEquals(
"[APIMessage [messageCode=1001, systemMessage=FORBIDDEN, details=No edit right grant for user: TestInstAdmin, attributes=[]]]", "[APIMessage [messageCode=1001, systemMessage=FORBIDDEN, details=No edit right grant for user: TestInstAdmin, attributes=[]]]",
String.valueOf(error.getErrorMessages())); String.valueOf(error.getAPIMessages()));
// change password // change password
final Result<UserInfo> passwordChange = restService final Result<UserInfo> passwordChange = restService
@ -400,7 +400,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
error = (RestCallError) instNames.getError(); error = (RestCallError) instNames.getError();
assertEquals( assertEquals(
"UNAUTHORIZED", "UNAUTHORIZED",
String.valueOf(error.getErrorMessages().get(0).getSystemMessage())); String.valueOf(error.getAPIMessages().get(0).getSystemMessage()));
// login again with the new password and check roles // login again with the new password and check roles
restService = createRestServiceForUser( restService = createRestServiceForUser(
@ -2339,7 +2339,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("Forbidden error message expected here"); fail("Forbidden error message expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1001", apiMessage.messageCode); assertEquals("1001", apiMessage.messageCode);
assertEquals("FORBIDDEN", apiMessage.systemMessage); assertEquals("FORBIDDEN", apiMessage.systemMessage);
} }
@ -2395,7 +2395,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2408,7 +2408,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2419,7 +2419,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2430,7 +2430,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2441,7 +2441,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2452,7 +2452,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }
@ -2463,7 +2463,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
.getOrThrow(); .getOrThrow();
fail("no resource found exception expected here"); fail("no resource found exception expected here");
} catch (final RestCallError e) { } catch (final RestCallError e) {
final APIMessage apiMessage = e.getErrorMessages().get(0); final APIMessage apiMessage = e.getAPIMessages().get(0);
assertEquals("1002", apiMessage.getMessageCode()); assertEquals("1002", apiMessage.getMessageCode());
assertEquals("resource not found", apiMessage.getSystemMessage()); assertEquals("resource not found", apiMessage.getSystemMessage());
} }