scoped programmatic i18n-keys (#190)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/190 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.config;
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import lombok.val;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -10,6 +12,7 @@ import java.util.Locale;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequestScope
|
@RequestScope
|
||||||
|
@Slf4j
|
||||||
public class MessageTranslator {
|
public class MessageTranslator {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -21,17 +24,24 @@ public class MessageTranslator {
|
|||||||
public String translateTo(final Locale locale, final String messageKey, final Object... args) {
|
public String translateTo(final Locale locale, final String messageKey, final Object... args) {
|
||||||
try {
|
try {
|
||||||
// we don't use the method which also takes a default message right away ...
|
// we don't use the method which also takes a default message right away ...
|
||||||
final var translatedMessage = messageSource.getMessage(messageKey, args, locale);
|
val translatedMessage = messageSource.getMessage(messageKey, args, locale);
|
||||||
return translatedMessage;
|
return translatedMessage;
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
final var defaultMessage = messageKey.replace("'", "''");
|
// ... but log the missing translation ...
|
||||||
final var translatedMessage = messageSource.getMessage(messageKey, args, defaultMessage, locale);
|
log.error("Missing translation for message key '{}' in locale '{}'", messageKey, locale, e);
|
||||||
if (locale != Locale.ENGLISH) {
|
|
||||||
// ... because we want to add a hint that the translation is missing, even if placeholders got replaced
|
// and decorate the default message to mark it as not really translated:
|
||||||
return translatedMessage + " [" + locale + " translation missing]";
|
val defaultMessage = messageKey.substring(messageKey.indexOf('.') + 1)
|
||||||
|
.replaceAll("--+", " - ")
|
||||||
|
.replaceAll("(?<! )-(?! )", " ")
|
||||||
|
.replace("'", "''");
|
||||||
|
val fallbackMessage = messageSource.getMessage(messageKey, args, defaultMessage, Locale.ENGLISH);
|
||||||
|
return decorateMissingTranslation(fallbackMessage);
|
||||||
}
|
}
|
||||||
return translatedMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String decorateMissingTranslation(final String translatedMessage) {
|
||||||
|
return "【⍰" + translatedMessage + "⍰】";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String translate(final String messageKey, final Object... args) {
|
public String translate(final String messageKey, final Object... args) {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public class MessagesResourceConfig {
|
|||||||
final var source = new ResourceBundleMessageSource();
|
final var source = new ResourceBundleMessageSource();
|
||||||
source.setBasenames("i18n/messages");
|
source.setBasenames("i18n/messages");
|
||||||
source.setDefaultEncoding("UTF-8");
|
source.setDefaultEncoding("UTF-8");
|
||||||
|
source.setFallbackToSystemLocale(false);
|
||||||
|
source.setUseCodeAsDefaultMessage(false);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class RealCasAuthenticator implements CasAuthenticator {
|
|||||||
|
|
||||||
private Document verifyServiceTicket(final String serviceTicket) throws SAXException, IOException, ParserConfigurationException {
|
private Document verifyServiceTicket(final String serviceTicket) throws SAXException, IOException, ParserConfigurationException {
|
||||||
if ( !serviceTicket.startsWith("ST-") ) {
|
if ( !serviceTicket.startsWith("ST-") ) {
|
||||||
throwBadCredentialsException("unknown authorization ticket");
|
throwBadCredentialsException("auth.unknown-authorization-ticket");
|
||||||
}
|
}
|
||||||
|
|
||||||
final var url = casServerUrl + "/cas/p3/serviceValidate" +
|
final var url = casServerUrl + "/cas/p3/serviceValidate" +
|
||||||
@@ -74,7 +74,7 @@ public class RealCasAuthenticator implements CasAuthenticator {
|
|||||||
private String extractUserName(final Document verification) {
|
private String extractUserName(final Document verification) {
|
||||||
|
|
||||||
if (verification.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
if (verification.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
||||||
throwBadCredentialsException("CAS service-ticket could not be verified");
|
throwBadCredentialsException("auth.cas-service-ticket-could-not-be-verified");
|
||||||
}
|
}
|
||||||
return verification.getElementsByTagName("cas:user").item(0).getTextContent();
|
return verification.getElementsByTagName("cas:user").item(0).getTextContent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import static java.util.Locale.ENGLISH;
|
|||||||
|
|
||||||
public class ReferenceNotFoundException extends RuntimeException {
|
public class ReferenceNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
private final String TRANSLATABLE_MESSAGE = "{0} \"{1}\" not found";
|
private final String TRANSLATABLE_MESSAGE = "general.{0}-{1}-not-found";
|
||||||
|
|
||||||
private final MessageTranslator translator;
|
private final MessageTranslator translator;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -156,7 +156,7 @@ public class RestResponseEntityExceptionHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Function<FieldError, String> toEnrichedFieldErrorMessage() {
|
private Function<FieldError, String> toEnrichedFieldErrorMessage() {
|
||||||
final var translatedButIsLiteral = messageTranslator.translate("but is");
|
final var translatedButIsLiteral = messageTranslator.translate("general.but-is");
|
||||||
// TODO.i18n: the following does not work in all languages, e.g. not in right-to-left languages
|
// TODO.i18n: the following does not work in all languages, e.g. not in right-to-left languages
|
||||||
return fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage() +
|
return fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage() +
|
||||||
" " + translatedButIsLiteral + " " + optionallyQuoted(fieldError.getRejectedValue());
|
" " + translatedButIsLiteral + " " + optionallyQuoted(fieldError.getRejectedValue());
|
||||||
|
|||||||
+4
-2
@@ -52,13 +52,15 @@ public class CredentialContextResourceToEntityMapper {
|
|||||||
final var existingContextEntity = em.find(HsCredentialsContextRealEntity.class, resource.getUuid());
|
final var existingContextEntity = em.find(HsCredentialsContextRealEntity.class, resource.getUuid());
|
||||||
if (existingContextEntity == null) {
|
if (existingContextEntity == null) {
|
||||||
throw new EntityNotFoundException(
|
throw new EntityNotFoundException(
|
||||||
messageTranslator.translate("{0} \"{1}\" not found or not accessible",
|
messageTranslator.translate(
|
||||||
|
"general.{0}-{1}-not-found-or-not-accessible",
|
||||||
"credentials uuid", resource.getUuid()));
|
"credentials uuid", resource.getUuid()));
|
||||||
}
|
}
|
||||||
if ((resource.getType() != null && !existingContextEntity.getType().equals(resource.getType())) ||
|
if ((resource.getType() != null && !existingContextEntity.getType().equals(resource.getType())) ||
|
||||||
(resource.getQualifier() != null && !existingContextEntity.getQualifier().equals(resource.getQualifier()))) {
|
(resource.getQualifier() != null && !existingContextEntity.getQualifier().equals(resource.getQualifier()))) {
|
||||||
throw new EntityNotFoundException(
|
throw new EntityNotFoundException(
|
||||||
messageTranslator.translate("existing {0} does not match given resource {1}",
|
messageTranslator.translate(
|
||||||
|
"credentials.existing-{0}-does-not-match-given-resource-{1}",
|
||||||
existingContextEntity, resource));
|
existingContextEntity, resource));
|
||||||
}
|
}
|
||||||
entities.add(existingContextEntity);
|
entities.add(existingContextEntity);
|
||||||
|
|||||||
@@ -203,12 +203,12 @@ public class HsCredentialsController implements CredentialsApi {
|
|||||||
private void validate(final HsCredentialsEntity newCredentialsEntity) {
|
private void validate(final HsCredentialsEntity newCredentialsEntity) {
|
||||||
// the referenced person must be represented by currently logged in person
|
// the referenced person must be represented by currently logged in person
|
||||||
final var personUuid = newCredentialsEntity.getPerson().getUuid();
|
final var personUuid = newCredentialsEntity.getPerson().getUuid();
|
||||||
final var representedPersonUuids = rbacPersonRepo.findPersonsrepresentedByPersonWithUuid(personUuid)
|
final var representedPersonUuids = rbacPersonRepo.findPersonsRepresentedByPersonWithUuid(personUuid)
|
||||||
.stream().map(HsOfficePerson::getUuid).toList();
|
.stream().map(HsOfficePerson::getUuid).toList();
|
||||||
if ( !representedPersonUuids.contains(personUuid)) {
|
if ( !representedPersonUuids.contains(personUuid)) {
|
||||||
throw new ValidationException(
|
throw new ValidationException(
|
||||||
messageTranslator.translate(
|
messageTranslator.translate(
|
||||||
"access-denied-personUuid-{0}-not-represented-by-currently-logged-in-person",
|
"credentials.access-denied-person-uuid-{0}-not-represented-by-currently-logged-in-person",
|
||||||
personUuid));
|
personUuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ public class HsCredentialsController implements CredentialsApi {
|
|||||||
private List<HsCredentialsEntity> findByPersonUuid(final UUID personUuid) {
|
private List<HsCredentialsEntity> findByPersonUuid(final UUID personUuid) {
|
||||||
final var person = rbacPersonRepo.findByUuid(personUuid).orElseThrow(
|
final var person = rbacPersonRepo.findByUuid(personUuid).orElseThrow(
|
||||||
() -> new EntityNotFoundException(
|
() -> new EntityNotFoundException(
|
||||||
messageTranslator.translate("{0} \"{1}\" not found or not accessible", "personUuid", personUuid)
|
messageTranslator.translate("general.{0}-{1}-not-found-or-not-accessible", "personUuid", personUuid)
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
@@ -269,7 +269,7 @@ public class HsCredentialsController implements CredentialsApi {
|
|||||||
final BiConsumer<CredentialsInsertResource, HsCredentialsEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<CredentialsInsertResource, HsCredentialsEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
final var person = rbacPersonRepo.findByUuid(resource.getPersonUuid()).orElseThrow(
|
final var person = rbacPersonRepo.findByUuid(resource.getPersonUuid()).orElseThrow(
|
||||||
() -> new EntityNotFoundException(
|
() -> new EntityNotFoundException(
|
||||||
messageTranslator.translate("{0} \"{1}\" not found or not accessible", "personUuid", resource.getPersonUuid())
|
messageTranslator.translate("general.{0}-{1}-not-found-or-not-accessible", "personUuid", resource.getPersonUuid())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ public class HsCoopAssetTranslations implements RetroactiveTranslator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTranslate(final String message) {
|
public boolean canTranslate(final String message) {
|
||||||
return message.equals("ERROR: [400] coop assets transaction would result in a negative balance of assets");
|
return message.equals("ERROR: [400] office.coop-assets.transaction-would-result-in-a-negative-balance-of-assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -26,7 +26,7 @@ public class HsCoopAssetTranslations implements RetroactiveTranslator {
|
|||||||
// and in this case it's just one
|
// and in this case it's just one
|
||||||
return ERROR_400_PREFIX + messageTranslator.translate(message.substring(ERROR_400_PREFIX.length()));
|
return ERROR_400_PREFIX + messageTranslator.translate(message.substring(ERROR_400_PREFIX.length()));
|
||||||
|
|
||||||
// HOWTO extract variable parts from a messages which got created without i18n support:
|
// HOWTO extract variable parts from messages which got created without i18n support:
|
||||||
// final var regex = "(?<propertyName>[^ ]+) (?<propertyValue>.+) not found";
|
// final var regex = "(?<propertyName>[^ ]+) (?<propertyValue>.+) not found";
|
||||||
// final var pattern = Pattern.compile(regex);
|
// final var pattern = Pattern.compile(regex);
|
||||||
// final var matcher = pattern.matcher(message);
|
// final var matcher = pattern.matcher(message);
|
||||||
|
|||||||
+15
-13
@@ -141,7 +141,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (List.of(DEPOSIT, HsOfficeCoopAssetsTransactionTypeResource.ADOPTION).contains(requestBody.getTransactionType())
|
if (List.of(DEPOSIT, HsOfficeCoopAssetsTransactionTypeResource.ADOPTION).contains(requestBody.getTransactionType())
|
||||||
&& requestBody.getAssetValue().signum() < 0) {
|
&& requestBody.getAssetValue().signum() < 0) {
|
||||||
violations.add(messageTranslator.translate("for transactionType={0}, assetValue must be positive but is {1,number,#0.00}",
|
violations.add(messageTranslator.translate(
|
||||||
|
"office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-positive-but-is-{1,number,#0.00}",
|
||||||
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
if (List.of(DISBURSAL, HsOfficeCoopAssetsTransactionTypeResource.TRANSFER, CLEARING, LOSS)
|
if (List.of(DISBURSAL, HsOfficeCoopAssetsTransactionTypeResource.TRANSFER, CLEARING, LOSS)
|
||||||
.contains(requestBody.getTransactionType())
|
.contains(requestBody.getTransactionType())
|
||||||
&& requestBody.getAssetValue().signum() > 0) {
|
&& requestBody.getAssetValue().signum() > 0) {
|
||||||
violations.add(messageTranslator.translate("for transactionType={0}, assetValue must be negative but is {1,number,#0.00}",
|
violations.add(messageTranslator.translate(
|
||||||
|
"office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-negative-but-is-{1,number,#0.00}",
|
||||||
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +163,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getAssetValue().signum() == 0) {
|
if (requestBody.getAssetValue().signum() == 0) {
|
||||||
violations.add(messageTranslator.translate("assetValue must not be 0"));
|
violations.add(messageTranslator.translate("office.coop-assets.assetvalue-must-not-be-0"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,18 +229,18 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
resource.getMembershipUuid()))
|
resource.getMembershipUuid()))
|
||||||
.orElseThrow(() -> new EntityNotFoundException(
|
.orElseThrow(() -> new EntityNotFoundException(
|
||||||
messageTranslator.translate(
|
messageTranslator.translate(
|
||||||
"{0} \"{1}\" not found", "membership.uuid", resource.getMembershipUuid())));
|
"general.{0}-{1}-not-found", "membership.uuid", resource.getMembershipUuid())));
|
||||||
entity.setMembership(membership);
|
entity.setMembership(membership);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.getTransactionType() == REVERSAL) {
|
if (entity.getTransactionType() == REVERSAL) {
|
||||||
if (resource.getRevertedAssetTxUuid() == null) {
|
if (resource.getRevertedAssetTxUuid() == null) {
|
||||||
throw new ValidationException(messageTranslator.translate(
|
throw new ValidationException(messageTranslator.translate(
|
||||||
"a REVERSAL asset transaction requires specifying a revertedAssetTx.uuid"));
|
"office.coop-assets.a-reversal-asset-transaction-requires-specifying-a-revertedassettx-uuid"));
|
||||||
}
|
}
|
||||||
final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
||||||
.orElseThrow(() -> new EntityNotFoundException(messageTranslator.translate(
|
.orElseThrow(() -> new EntityNotFoundException(messageTranslator.translate(
|
||||||
"{0} \"{1}\" not found",
|
"general.{0}-{1}-not-found",
|
||||||
"revertedAssetTx.uuid",
|
"revertedAssetTx.uuid",
|
||||||
resource.getRevertedAssetTxUuid())));
|
resource.getRevertedAssetTxUuid())));
|
||||||
revertedAssetTx.setReversalAssetTx(entity);
|
revertedAssetTx.setReversalAssetTx(entity);
|
||||||
@@ -246,7 +248,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
||||||
throw new ValidationException(
|
throw new ValidationException(
|
||||||
messageTranslator.translate(
|
messageTranslator.translate(
|
||||||
"given assetValue {0,number,#0.00} must be the negative value of the reverted asset transaction: {1,number,#0.00}",
|
"office.coop-assets.given-assetvalue-{0,number,#0.00}-must-be-the-negative-value-of-the-reverted-asset-transaction-{1,number,#0.00}",
|
||||||
resource.getAssetValue(), revertedAssetTx.getAssetValue()));
|
resource.getAssetValue(), revertedAssetTx.getAssetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +272,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
final var adoptingMembership = determineAdoptingMembership(resource);
|
final var adoptingMembership = determineAdoptingMembership(resource);
|
||||||
if ( entity.getMembership() == adoptingMembership) {
|
if ( entity.getMembership() == adoptingMembership) {
|
||||||
throw new ValidationException(messageTranslator.translate(
|
throw new ValidationException(messageTranslator.translate(
|
||||||
"transferring and adopting membership must be different, but both are {0}",
|
"office.coop-assets.transferring-and-adopting-membership-must-be-different-but-both-are-{0}",
|
||||||
adoptingMembership.getTaggedMemberNumber()));
|
adoptingMembership.getTaggedMemberNumber()));
|
||||||
}
|
}
|
||||||
final var adoptingAssetTx = createAdoptingAssetTx(entity, adoptingMembership);
|
final var adoptingAssetTx = createAdoptingAssetTx(entity, adoptingMembership);
|
||||||
@@ -285,8 +287,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
// @formatter:off
|
// @formatter:off
|
||||||
final var message = messageTranslator.translate(
|
final var message = messageTranslator.translate(
|
||||||
resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER
|
resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER
|
||||||
? "either {0} or {1} must be given, not both"
|
? "office.coop-assets.either-{0}-or-{1}-must-be-given-not-both"
|
||||||
: "neither {0} nor {1} must be given for transactionType={2}",
|
: "office.coop-assets.neither-{0}-nor-{1}-must-be-given-for-transactiontype-{2}",
|
||||||
"adoptingMembership.uuid",
|
"adoptingMembership.uuid",
|
||||||
"adoptingMembership.memberNumber",
|
"adoptingMembership.memberNumber",
|
||||||
resource.getTransactionType());
|
resource.getTransactionType());
|
||||||
@@ -298,7 +300,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
final var adoptingMembership = membershipRepo.findByUuid(adoptingMembershipUuid);
|
final var adoptingMembership = membershipRepo.findByUuid(adoptingMembershipUuid);
|
||||||
return adoptingMembership.orElseThrow(() ->
|
return adoptingMembership.orElseThrow(() ->
|
||||||
new ValidationException(messageTranslator.translate(
|
new ValidationException(messageTranslator.translate(
|
||||||
"{0} \"{1}\" not found or not accessible",
|
"general.{0}-{1}-not-found-or-not-accessible",
|
||||||
"adoptingMembership.uuid",
|
"adoptingMembership.uuid",
|
||||||
adoptingMembershipUuid)));
|
adoptingMembershipUuid)));
|
||||||
}
|
}
|
||||||
@@ -309,14 +311,14 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
return adoptingMembership.orElseThrow( () ->
|
return adoptingMembership.orElseThrow( () ->
|
||||||
new ValidationException(
|
new ValidationException(
|
||||||
messageTranslator.translate(
|
messageTranslator.translate(
|
||||||
"{0} \"{1}\" not found or not accessible",
|
"general.{0}-{1}-not-found-or-not-accessible",
|
||||||
"adoptingMembership.memberNumber",
|
"adoptingMembership.memberNumber",
|
||||||
adoptingMembershipMemberNumber)));
|
adoptingMembershipMemberNumber)));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ValidationException(
|
throw new ValidationException(
|
||||||
messageTranslator.translate(
|
messageTranslator.translate(
|
||||||
"either {0} or {1} must be given for transactionType={2}",
|
"office.coop-assets.either-{0}-or-{1}-must-be-given-for-transactiontype-{2}",
|
||||||
"adoptingMembership.uuid",
|
"adoptingMembership.uuid",
|
||||||
"adoptingMembership.memberNumber",
|
"adoptingMembership.memberNumber",
|
||||||
resource.getTransactionType()
|
resource.getTransactionType()
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ public class HsCoopShareTranslations implements RetroactiveTranslator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTranslate(final String message) {
|
public boolean canTranslate(final String message) {
|
||||||
return message.equals("ERROR: [400] coop shares transaction would result in a negative number of shares");
|
return message.equals("ERROR: [400] office.coop-shares.transaction-would-result-in-a-negative-number-of-shares");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+5
-3
@@ -129,7 +129,8 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getTransactionType() == SUBSCRIPTION
|
if (requestBody.getTransactionType() == SUBSCRIPTION
|
||||||
&& requestBody.getShareCount() < 0) {
|
&& requestBody.getShareCount() < 0) {
|
||||||
violations.add(messageTranslator.translate("for transactionType={0}, shareCount must be positive but is {1}",
|
violations.add(messageTranslator.translate(
|
||||||
|
"office.coop-shares.for-transactiontype-{0}-sharecount-must-be-positive-but-is-{1}",
|
||||||
requestBody.getTransactionType(), requestBody.getShareCount()));
|
requestBody.getTransactionType(), requestBody.getShareCount()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +140,8 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getTransactionType() == CANCELLATION
|
if (requestBody.getTransactionType() == CANCELLATION
|
||||||
&& requestBody.getShareCount() > 0) {
|
&& requestBody.getShareCount() > 0) {
|
||||||
violations.add(messageTranslator.translate("for transactionType={0}, shareCount must be negative but is {1}",
|
violations.add(messageTranslator.translate(
|
||||||
|
"office.coop-shares.for-transactiontype-{0}-sharecount-must-be-negative-but-is-{1}",
|
||||||
requestBody.getTransactionType(), requestBody.getShareCount()));
|
requestBody.getTransactionType(), requestBody.getShareCount()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +150,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getShareCount() == 0) {
|
if (requestBody.getShareCount() == 0) {
|
||||||
violations.add(messageTranslator.translate("shareCount must not be 0"));
|
violations.add(messageTranslator.translate("office.coop-shares.sharecount-must-not-be-0"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
|||||||
context.assumeRoles(assumedRoles);
|
context.assumeRoles(assumedRoles);
|
||||||
|
|
||||||
final var entities = representedByPersonUuid != null
|
final var entities = representedByPersonUuid != null
|
||||||
? personRepo.findPersonsrepresentedByPersonWithUuid(representedByPersonUuid)
|
? personRepo.findPersonsRepresentedByPersonWithUuid(representedByPersonUuid)
|
||||||
: personRepo.findPersonByOptionalNameLike(name);
|
: personRepo.findPersonByOptionalNameLike(name);
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficePersonResource.class);
|
final var resources = mapper.mapList(entities, HsOfficePersonResource.class);
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ public interface HsOfficePersonRbacRepository extends Repository<HsOfficePersonR
|
|||||||
OR person.uuid = :personUuid
|
OR person.uuid = :personUuid
|
||||||
""", nativeQuery = true)
|
""", nativeQuery = true)
|
||||||
@Timed("app.office.persons.repo.findRepresentedPersons.rbac")
|
@Timed("app.office.persons.repo.findRepresentedPersons.rbac")
|
||||||
List<HsOfficePersonRbacEntity> findPersonsrepresentedByPersonWithUuid(UUID personUuid);
|
List<HsOfficePersonRbacEntity> findPersonsRepresentedByPersonWithUuid(UUID personUuid);
|
||||||
|
|
||||||
@Timed("app.office.persons.repo.save.rbac")
|
@Timed("app.office.persons.repo.save.rbac")
|
||||||
HsOfficePersonRbacEntity save(final HsOfficePersonRbacEntity entity);
|
HsOfficePersonRbacEntity save(final HsOfficePersonRbacEntity entity);
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ public class PingController implements TestApi {
|
|||||||
@PreAuthorize("permitAll()")
|
@PreAuthorize("permitAll()")
|
||||||
@Timed("app.api.ping")
|
@Timed("app.api.ping")
|
||||||
public ResponseEntity<String> ping() {
|
public ResponseEntity<String> ping() {
|
||||||
final var userName = SecurityContextHolder.getContext().getAuthentication().getName();
|
|
||||||
// HOWTO translate text with placeholders - also see in resource files i18n/messages_*.properties.
|
// HOWTO translate text with placeholders - also see in resource files i18n/messages_*.properties.
|
||||||
final var translatedMessage = messageTranslator.translate("pong {0} - in English", userName);
|
final var translatedMessage = messageTranslator.translate("test.pinged--in-your-language");
|
||||||
return ResponseEntity.ok(translatedMessage + "\n");
|
return ResponseEntity.ok(translatedMessage + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ metrics:
|
|||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
org.springframework.security: info
|
org.springframework.security: info
|
||||||
org.springframework.web: DEBUG
|
# org.springframework.web: DEBUG
|
||||||
org.springframework.web.method.annotation: DEBUG
|
# org.springframework.web.method.annotation: DEBUG
|
||||||
org.springframework.validation: DEBUG
|
# org.springframework.validation: DEBUG
|
||||||
# HOWTO configure logging, e.g. logging to a separate file, see:
|
# HOWTO configure logging, e.g. logging to a separate file, see:
|
||||||
# https://docs.spring.io/spring-boot/reference/features/logging.html
|
# https://docs.spring.io/spring-boot/reference/features/logging.html
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -33,7 +33,7 @@ alter table hs_office.coopsharetx
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset marc.sandlus:hs-office-coopshares-SHARE-COUNT-CONSTRAINT-BY-TRIGGER endDelimiter:--//
|
--changeset marc.sandlus:hs-office-coopshares-SHARE-COUNT-CONSTRAINT-BY-TRIGGER runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
alter table hs_office.coopsharetx drop constraint if exists check_positive_total_shares_count;
|
alter table hs_office.coopsharetx drop constraint if exists check_positive_total_shares_count;
|
||||||
@@ -42,7 +42,6 @@ drop function if exists hs_office.coopsharestx_check_positive_total cascade;
|
|||||||
|
|
||||||
create or replace function hs_office.coopsharetx_enforce_positive_total()
|
create or replace function hs_office.coopsharetx_enforce_positive_total()
|
||||||
returns trigger as $$
|
returns trigger as $$
|
||||||
|
|
||||||
declare
|
declare
|
||||||
currentShareCount integer;
|
currentShareCount integer;
|
||||||
totalShareCount integer;
|
totalShareCount integer;
|
||||||
@@ -53,12 +52,13 @@ begin
|
|||||||
into currentShareCount;
|
into currentShareCount;
|
||||||
totalShareCount := currentShareCount + NEW.shareCount;
|
totalShareCount := currentShareCount + NEW.shareCount;
|
||||||
if totalShareCount < 0 then
|
if totalShareCount < 0 then
|
||||||
raise exception '[400] coop shares transaction would result in a negative number of shares';
|
raise exception '[400] office.coop-shares.transaction-would-result-in-a-negative-number-of-shares';
|
||||||
end if;
|
end if;
|
||||||
return NEW;
|
return NEW;
|
||||||
end;
|
end;
|
||||||
$$ LANGUAGE plpgsql;;
|
$$ LANGUAGE plpgsql;;
|
||||||
|
|
||||||
|
drop trigger if exists positive_total_shares_count_tg on hs_office.coopsharetx;
|
||||||
|
|
||||||
create trigger positive_total_shares_count_tg before insert
|
create trigger positive_total_shares_count_tg before insert
|
||||||
on hs_office.coopsharetx
|
on hs_office.coopsharetx
|
||||||
|
|||||||
+4
-3
@@ -73,7 +73,7 @@ CREATE TRIGGER enforce_transaction_constraints
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset marc.sandlus:hs-office-coopassets-ASSET-VALUE-CONSTRAINT-BY-TRIGGER endDelimiter:--//
|
--changeset marc.sandlus:hs-office-coopassets-ASSET-VALUE-CONSTRAINT-BY-TRIGGER runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
alter table hs_office.coopassettx
|
alter table hs_office.coopassettx
|
||||||
@@ -83,7 +83,6 @@ drop function if exists hs_office.coopassetstx_check_positive_total cascade;
|
|||||||
|
|
||||||
create or replace function hs_office.coopassettx_enforce_positive_total()
|
create or replace function hs_office.coopassettx_enforce_positive_total()
|
||||||
returns trigger as $$
|
returns trigger as $$
|
||||||
|
|
||||||
declare
|
declare
|
||||||
currentAssetValue numeric(12,2);
|
currentAssetValue numeric(12,2);
|
||||||
totalAssetValue numeric(12,2);
|
totalAssetValue numeric(12,2);
|
||||||
@@ -94,12 +93,14 @@ begin
|
|||||||
into currentAssetValue;
|
into currentAssetValue;
|
||||||
totalAssetValue := currentAssetValue + NEW.assetValue;
|
totalAssetValue := currentAssetValue + NEW.assetValue;
|
||||||
if totalAssetValue::numeric < 0 then
|
if totalAssetValue::numeric < 0 then
|
||||||
raise exception '[400] coop assets transaction would result in a negative balance of assets';
|
raise exception '[400] office.coop-assets.transaction-would-result-in-a-negative-balance-of-assets';
|
||||||
end if;
|
end if;
|
||||||
return NEW;
|
return NEW;
|
||||||
end;
|
end;
|
||||||
$$ LANGUAGE plpgsql;;
|
$$ LANGUAGE plpgsql;;
|
||||||
|
|
||||||
|
drop trigger if exists positive_total_assets_count_tg on hs_office.coopassettx;
|
||||||
|
|
||||||
create trigger positive_total_assets_count_tg before insert
|
create trigger positive_total_assets_count_tg before insert
|
||||||
on hs_office.coopassettx
|
on hs_office.coopassettx
|
||||||
for each row execute function hs_office.coopassettx_enforce_positive_total();
|
for each row execute function hs_office.coopassettx_enforce_positive_total();
|
||||||
|
|||||||
@@ -1,36 +1,38 @@
|
|||||||
# This file must be in UTF-8 encoding. Check if you see umlauts or garbage: äöüÄÖÜß.
|
# This file must be in UTF-8 encoding. Check if you see umlauts or garbage: äöüÄÖÜß.
|
||||||
# HINT IntelliJ IDEA shows unused keys in gray.
|
# HINT IntelliJ IDEA shows unused keys in gray.
|
||||||
|
|
||||||
pong\ {0}\ -\ in\ English=pong {0} - auf Deutsch
|
test.pinged--in-your-language=pinged - auf Deutsch
|
||||||
|
test.ponged-{0}--in-your-language=ponged {0} - auf Deutsch
|
||||||
|
test.available-in-all-properties-files=Hallo {0} - DE!
|
||||||
|
|
||||||
# config (including authorization)
|
# authorization
|
||||||
CAS\ service-ticket\ could\ not\ be\ verified=CAS Service-Ticket konnte nicht verifiziert werden
|
auth.cas-service-ticket-could-not-be-verified=CAS Service-Ticket konnte nicht verifiziert werden
|
||||||
unknown\ authorization\ ticket=unbekanntes Autorisierungs-Ticket
|
auth.unknown-authorization-ticket=unbekanntes Autorisierungs-Ticket
|
||||||
|
|
||||||
# general validations
|
# general validations
|
||||||
{0}\ "{1}"\ not\ found={0} "{1}" nicht gefunden
|
general.{0}-{1}-not-found={0} "{1}" nicht gefunden
|
||||||
{0}\ "{1}"\ not\ found\ or\ not\ accessible={0} "{1}" nicht gefunden oder nicht zugänglich
|
general.{0}-{1}-not-found-or-not-accessible={0} "{1}" nicht gefunden oder nicht zugänglich
|
||||||
but\ is=ist aber
|
general.but-is=ist aber
|
||||||
|
|
||||||
# credentials validations
|
# credentials validations
|
||||||
existing\ {0}\ does\ not\ match\ given\ resource\ {1}=existierender Credentials-Context {0} passt nicht zum angegebenen {1}
|
credentials.existing-{0}-does-not-match-given-resource-{1}=existierender Credentials-Context {0} passt nicht zum angegebenen {1}
|
||||||
access-denied-personUuid-{0}-not-represented-by-currently-logged-in-person=Zugriff verweigert: personUuid "{0}" wird von der eingeloggten Person nicht repräsentiert
|
credentials.access-denied-person-uuid-{0}-not-represented-by-currently-logged-in-person=Zugriff verweigert: personUuid "{0}" wird von der eingeloggten Person nicht repräsentiert
|
||||||
|
|
||||||
# office.coop-shares
|
# office.coop-shares
|
||||||
for\ transactionType\={0},\ shareCount\ must\ be\ positive\ but\ is\ {1}=für transactionType={0}, muss shareCount positiv sein, ist aber {1}
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-positive-but-is-{1}=für transactionType={0}, muss shareCount positiv sein, ist aber {1}
|
||||||
for\ transactionType\={0},\ shareCount\ must\ be\ negative\ but\ is\ {1}=für transactionType={0}, muss shareCount negativ sein, ist aber {1}
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-negative-but-is-{1}=für transactionType={0}, muss shareCount negativ sein, ist aber {1}
|
||||||
shareCount\ must\ not\ be\ 0=shareCount darf nicht 0 sein
|
office.coop-shares.sharecount-must-not-be-0=shareCount darf nicht 0 sein
|
||||||
coop\ shares\ transaction\ would\ result\ in\ a\ negative\ number\ of\ shares=Geschäftsanteile-Transaktion würde zu negativen Geschäftsanteilen führen
|
office.coop-shares.transaction-would-result-in-a-negative-number-of-shares=Geschäftsanteile-Transaktion würde zu negativen Geschäftsanteilen führen
|
||||||
|
|
||||||
# office.coop-assets
|
# office.coop-assets
|
||||||
either\ {0}\ or\ {1}\ must\ be\ given=entweder {0} oder {1} muss angegeben werden
|
office.coop-assets.either-{0}-or-{1}-must-be-given=entweder {0} oder {1} muss angegeben werden
|
||||||
either\ {0}\ or\ {1}\ must\ be\ given,\ not\ both=entweder {0} oder {1} muss angegeben werden, nicht beide
|
office.coop-assets.either-{0}-or-{1}-must-be-given-not-both=entweder {0} oder {1} muss angegeben werden, nicht beide
|
||||||
either\ {0}\ or\ {1}\ must\ be\ given\ for\ transactionType\={2}=für transactionType={2} muss entweder {0} oder {1} angegeben werden
|
office.coop-assets.either-{0}-or-{1}-must-be-given-for-transactiontype-{2}=für transactionType={2} muss entweder {0} oder {1} angegeben werden
|
||||||
neither\ {0}\ nor\ {1}\ must\ be\ given\ for\ transactionType\={2}=für transactionType={2} darf weder {0} noch {1} angegeben werden
|
office.coop-assets.neither-{0}-nor-{1}-must-be-given-for-transactiontype-{2}=für transactionType={2} darf weder {0} noch {1} angegeben werden
|
||||||
assetValue\ must\ not\ be\ 0=assetValue darf nicht 0 sein
|
office.coop-assets.assetvalue-must-not-be-0=assetValue darf nicht 0 sein
|
||||||
for\ transactionType\={0},\ assetValue\ must\ be\ positive\ but\ is\ {1,number,#0.00}=für transactionType={0}, muss assetValue positiv sein, ist aber {1,number,#0.00}
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-positive-but-is-{1,number,#0.00}=für transactionType={0}, muss assetValue positiv sein, ist aber {1,number,#0.00}
|
||||||
for\ transactionType\={0},\ assetValue\ must\ be\ negative\ but\ is\ {1,number,#0.00}=für transactionType={0}, muss assetValue negativ sein, ist aber {1,number,#0.00}
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-negative-but-is-{1,number,#0.00}=für transactionType={0}, muss assetValue negativ sein, ist aber {1,number,#0.00}
|
||||||
given\ assetValue\ {0,number,#0.00}\ must\ be\ the\ negative\ value\ of\ the\ reverted\ asset\ transaction\:\ {1,number,#0.00}=assetValue={0,number,#0.00} muss dem negativen Wert des Wertes der stornierten Geschäftsguthaben-Transaktion entsprechen: {1,number,#0.00}
|
office.coop-assets.given-assetvalue-{0,number,#0.00}-must-be-the-negative-value-of-the-reverted-asset-transaction-{1,number,#0.00}=assetValue={0,number,#0.00} muss dem negativen Wert des Wertes der stornierten Geschäftsguthaben-Transaktion entsprechen: {1,number,#0.00}
|
||||||
a\ REVERSAL\ asset\ transaction\ requires\ specifying\ a\ revertedAssetTx.uuid=eine REVERSAL Geschäftsguthaben-Transaktion erfordert die Angabe einer revertedAssetTx.uuid
|
office.coop-assets.a-reversal-asset-transaction-requires-specifying-a-revertedassettx-uuid=eine REVERSAL Geschäftsguthaben-Transaktion erfordert die Angabe einer revertedAssetTx.uuid
|
||||||
transferring\ and\ adopting\ membership\ must\ be\ different,\ but\ both\ are\ {0}=übertragende und annehmende Mitgliedschaft müssen unterschiedlich sein, aber beide sind {0}
|
office.coop-assets.transferring-and-adopting-membership-must-be-different-but-both-are-{0}=übertragende und annehmende Mitgliedschaft müssen unterschiedlich sein, aber beide sind {0}
|
||||||
coop\ assets\ transaction\ would\ result\ in\ a\ negative\ balance\ of\ assets=Geschäftsguthaben-Transaktion würde zu einem negativen Geschäftsguthaben-Saldo führen
|
office.coop-assets.transaction-would-result-in-a-negative-balance-of-assets=Geschäftsguthaben-Transaktion würde zu einem negativen Geschäftsguthaben-Saldo führen
|
||||||
|
|||||||
@@ -1,9 +1,39 @@
|
|||||||
# This file must be in UTF-8 encoding. Check if you see umlauts or garbage: äöüÄÖÜß
|
# This file must be in UTF-8 encoding. Check if you see umlauts or garbage: äöüÄÖÜß
|
||||||
# HINT IntelliJ IDEA shows unused keys in gray.
|
# HINT IntelliJ IDEA shows unused keys in gray.
|
||||||
|
|
||||||
# If the English translation is identical to the translation-key, it does not need to be included here.
|
test.pinged--in-your-language=pinged - in English
|
||||||
# But in that case, you can NOT use a prefix - or the prefix would be shown to the user as well.
|
test.ponged-{0}--in-your-language=ponged {0} - in English
|
||||||
# I'm not sure, though, if using the english default translations as keys is really a good idea.
|
test.available-in-all-properties-files=Hello {0} - EN!
|
||||||
|
|
||||||
|
# authorization
|
||||||
|
auth.cas-service-ticket-could-not-be-verified=CAS service-ticket could not be verified
|
||||||
|
auth.unknown-authorization-ticket=unknown authorization ticket
|
||||||
|
|
||||||
|
# general validations
|
||||||
|
general.{0}-{1}-not-found={0} "{1}" not found
|
||||||
|
general.{0}-{1}-not-found-or-not-accessible={0} "{1}" not found or not accessible
|
||||||
|
general.but-is=but is
|
||||||
|
|
||||||
# credentials validations
|
# credentials validations
|
||||||
access-denied-personUuid-{0}-not-represented-by-currently-logged-in-person=access denied: personUuid "{0}" not represented by currently logged in person
|
credentials.existing-{0}-does-not-match-given-resource-{1}=existing {0} does not match given resource {1}
|
||||||
|
credentials.access-denied-person-uuid-{0}-not-represented-by-currently-logged-in-person=access denied: personUuid "{0}" not represented by currently logged in person
|
||||||
|
|
||||||
|
# office.coop-shares
|
||||||
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-positive-but-is-{1}=for transactiontType {0} shareCount must be positive but is {1}
|
||||||
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-negative-but-is-{1}=for transactiontType {0} shareCount must be negative but is {1}
|
||||||
|
office.coop-shares.sharecount-must-not-be-0=shareCount must not be 0
|
||||||
|
office.coop-shares.transaction-would-result-in-a-negative-number-of-shares=coop shares transaction would result in a negative number of shares
|
||||||
|
|
||||||
|
# office.coop-assets
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given=either {0} or {1} must be given
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given-not-both=either {0} or {1} must be given not both
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given-for-transactiontype-{2}=either {0} or {1} must be given for transactionType {2}
|
||||||
|
office.coop-assets.neither-{0}-nor-{1}-must-be-given-for-transactiontype-{2}=neither {0} nor {1} must be given for transactionType {2}
|
||||||
|
office.coop-assets.assetvalue-must-not-be-0=assetvalue must not be 0
|
||||||
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-positive-but-is-{1,number,#0.00}=for transactionType {0} assetValue must be positive but is {1,number,#0.00}
|
||||||
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-negative-but-is-{1,number,#0.00}=for transactionType {0} assetValue must be negative but is {1,number,#0.00}
|
||||||
|
office.coop-assets.given-assetvalue-{0,number,#0.00}-must-be-the-negative-value-of-the-reverted-asset-transaction-{1,number,#0.00}=given assetValue {0,number,#0.00} must be the negative value of the reverted asset transaction {1,number,#0.00}
|
||||||
|
office.coop-assets.a-reversal-asset-transaction-requires-specifying-a-revertedassettx-uuid=a reversal asset transaction requires specifying a revertedAssetTx uuid
|
||||||
|
office.coop-assets.transferring-and-adopting-membership-must-be-different-but-both-are-{0}=transferring and adopting membership must be different but both are {0}
|
||||||
|
office.coop-assets.transaction-would-result-in-a-negative-balance-of-assets=coop assets transaction would result in a negative balance of assets
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# This file must be in UTF-8 encoding. Check if you see umlauts or garbage: äöüÄÖÜß
|
||||||
|
# HINT IntelliJ IDEA shows unused keys in gray.
|
||||||
|
|
||||||
|
# test.ponged--in-your-language=this translation is deliberately missing
|
||||||
|
test.pinged--in-your-language=ponged {0} - en Francais
|
||||||
|
test.available-in-all-properties-files=Salut {0} - FR!
|
||||||
|
|
||||||
|
# authorization
|
||||||
|
auth.cas-service-ticket-could-not-be-verified=Le ticket de service CAS n'a pas pu être vérifié
|
||||||
|
auth.unknown-authorization-ticket=ticket d'autorisation inconnu
|
||||||
|
|
||||||
|
# general validations
|
||||||
|
general.{0}-{1}-not-found={0} "{1}" non trouvé
|
||||||
|
general.{0}-{1}-not-found-or-not-accessible={0} "{1}" non trouvé ou non accessible
|
||||||
|
general.but-is=mais c'est
|
||||||
|
|
||||||
|
# credentials validations
|
||||||
|
credentials.existing-{0}-does-not-match-given-resource-{1}={0} existant ne correspond pas à la ressource donnée {1}
|
||||||
|
credentials.access-denied-person-uuid-{0}-not-represented-by-currently-logged-in-person=accès refusé : personUuid "{0}" non représenté par la personne actuellement connectée
|
||||||
|
|
||||||
|
# office.coop-shares
|
||||||
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-positive-but-is-{1}=pour le type de transaction {0}, shareCount doit être positif mais est {1}
|
||||||
|
office.coop-shares.for-transactiontype-{0}-sharecount-must-be-negative-but-is-{1}=pour le type de transaction {0}, shareCount doit être négatif mais est {1}
|
||||||
|
office.coop-shares.sharecount-must-not-be-0=shareCount ne doit pas être 0
|
||||||
|
office.coop-shares.transaction-would-result-in-a-negative-number-of-shares=la transaction de parts coopératives résulterait en un nombre négatif de parts
|
||||||
|
|
||||||
|
# office.coop-assets
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given=soit {0} soit {1} doit être fourni
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given-not-both=soit {0} soit {1} doit être fourni, pas les deux
|
||||||
|
office.coop-assets.either-{0}-or-{1}-must-be-given-for-transactiontype-{2}=soit {0} soit {1} doit être fourni pour le type de transaction {2}
|
||||||
|
office.coop-assets.neither-{0}-nor-{1}-must-be-given-for-transactiontype-{2}=ni {0} ni {1} ne doit être fourni pour le type de transaction {2}
|
||||||
|
office.coop-assets.assetvalue-must-not-be-0=assetvalue ne doit pas être 0
|
||||||
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-positive-but-is-{1,number,#0.00}=pour le type de transaction {0}, assetValue doit être positif mais est {1,number,#0.00}
|
||||||
|
office.coop-assets.for-transactiontype-{0}-assetvalue-must-be-negative-but-is-{1,number,#0.00}=pour le type de transaction {0}, assetValue doit être négatif mais est {1,number,#0.00}
|
||||||
|
office.coop-assets.given-assetvalue-{0,number,#0.00}-must-be-the-negative-value-of-the-reverted-asset-transaction-{1,number,#0.00}=assetValue donné {0,number,#0.00} doit être la valeur négative de la transaction d'actif annulée {1,number,#0.00}
|
||||||
|
office.coop-assets.a-reversal-asset-transaction-requires-specifying-a-revertedassettx-uuid=une transaction d'actif d'annulation nécessite de spécifier un uuid revertedAssetTx
|
||||||
|
office.coop-assets.transferring-and-adopting-membership-must-be-different-but-both-are-{0}=l'adhésion transférant et adoptante doit être différente, mais les deux sont {0}
|
||||||
|
office.coop-assets.transaction-would-result-in-a-negative-balance-of-assets=la transaction d'actifs coopératifs résulterait en un solde négatif d'actifs
|
||||||
+2
-2
@@ -69,7 +69,7 @@ class CasAuthenticationFilterIntegrationTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/pong",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(null, headers(entry("Authorization", "ST-valid"))),
|
new HttpEntity<>(null, headers(entry("Authorization", "ST-valid"))),
|
||||||
String.class
|
String.class
|
||||||
@@ -77,7 +77,7 @@ class CasAuthenticationFilterIntegrationTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(result.getBody()).startsWith("pong " + username);
|
assertThat(result.getBody()).startsWith("ponged " + username);
|
||||||
// HOWTO assert log messages
|
// HOWTO assert log messages
|
||||||
assertThat(capturedOutput.getOut()).containsPattern(
|
assertThat(capturedOutput.getOut()).containsPattern(
|
||||||
LogbackLogPattern.of(LogLevel.DEBUG, RealCasAuthenticator.class, "CAS-user: " + username));
|
LogbackLogPattern.of(LogLevel.DEBUG, RealCasAuthenticator.class, "CAS-user: " + username));
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
@SpringBootTest(classes = {
|
||||||
|
MessagesResourceConfig.class,
|
||||||
|
MessageTranslator.class
|
||||||
|
})
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
@Tag("generalIntegrationTest")
|
||||||
|
class MessageTranslatorIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebApplicationContext webApplicationContext;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private Context contextMock; // avoiding dependency issues
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
enum TestCases {
|
||||||
|
ENGLISH_KNOWN(Locale.ENGLISH, "test.ponged-{0}--in-your-language",
|
||||||
|
"ponged testUser - in English"),
|
||||||
|
ENGLISH_UNKNOWN(Locale.ENGLISH, "test.ponged-{0}--unknown-key",
|
||||||
|
"【⍰ponged testUser - unknown key⍰】"),
|
||||||
|
ENGLISH_US(Locale.of("en", "US"), "test.ponged-{0}--in-your-language",
|
||||||
|
"ponged testUser - in English"),
|
||||||
|
ENGLISH_UK(Locale.of("en", "UK"), "test.ponged-{0}--in-your-language",
|
||||||
|
"ponged testUser - in English"),
|
||||||
|
GERMAN_KNOWN(Locale.GERMAN, "test.ponged-{0}--in-your-language",
|
||||||
|
"ponged testUser - auf Deutsch"),
|
||||||
|
FRENCH_UNKNOWN_BUT_ENGLISH_KNOWN(Locale.FRENCH, "test.ponged-{0}--in-your-language",
|
||||||
|
"【⍰ponged testUser - in English⍰】"),
|
||||||
|
FRENCH_UNKNOWN_AND_ENGLISH_UNKNOWN(Locale.FRENCH, "test.ponged-{0}--unknown-key",
|
||||||
|
"【⍰ponged testUser - unknown key⍰】"),
|
||||||
|
UNKNOWN_LOCALE_AND_ENGLISH_KNOWN(Locale.TRADITIONAL_CHINESE,
|
||||||
|
"test.ponged-{0}--in-your-language", "【⍰ponged testUser - in English⍰】"),
|
||||||
|
UNKNOWN_LOCALE_AND_ENGLISH_UNKNOWN(Locale.TRADITIONAL_CHINESE, "test.ponged-{0}--unknown-key",
|
||||||
|
"【⍰ponged testUser - unknown key⍰】");
|
||||||
|
final Locale locale;
|
||||||
|
final String messageKey;
|
||||||
|
final String expectedTranslation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(TestCases.class)
|
||||||
|
void shouldHandleDifferentLocalesAppropriately(final TestCases testCase) {
|
||||||
|
// given
|
||||||
|
val messageTranslator = webApplicationContext.getBean(MessageTranslator.class);
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = messageTranslator.translateTo(testCase.locale, testCase.messageKey, "testUser");
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEqualTo(testCase.expectedTranslation);
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-8
@@ -67,14 +67,14 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
|
|
||||||
// http request
|
// http request
|
||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/pong",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
httpHeaders(entry("Authorization", "Bearer ST-fake-cas-ticket")),
|
httpHeaders(entry("Authorization", "Bearer ST-fake-cas-ticket")),
|
||||||
String.class
|
String.class
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(result.getBody()).startsWith("pong fake-user-name");
|
assertThat(result.getBody()).startsWith("ponged fake-user-name");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -85,18 +85,18 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
|
|
||||||
// http request
|
// http request
|
||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/pong",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
httpHeaders(entry("Authorization", "Bearer TGT-fake-cas-ticket")),
|
httpHeaders(entry("Authorization", "Bearer TGT-fake-cas-ticket")),
|
||||||
String.class
|
String.class
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(result.getBody()).startsWith("pong fake-user-name");
|
assertThat(result.getBody()).startsWith("ponged fake-user-name");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void accessToApiWithInvalidTicketGrantingTicketShouldBePermitted() {
|
void accessToOpenApiWithInvalidTicketGrantingTicketShouldBePermitted() {
|
||||||
// given
|
// given
|
||||||
givenCasServiceTicketForTicketGrantingTicket("TGT-fake-cas-ticket", "ST-fake-cas-ticket");
|
givenCasServiceTicketForTicketGrantingTicket("TGT-fake-cas-ticket", "ST-fake-cas-ticket");
|
||||||
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
@@ -113,14 +113,14 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void accessToPingApiWithoutTokenShouldBePermitted() {
|
void accessToOpenApiWithoutTokenShouldBePermitted() {
|
||||||
final var result = this.restTemplate.getForEntity(
|
final var result = this.restTemplate.getForEntity(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping", String.class);
|
"http://localhost:" + this.serverPort + "/api/ping", String.class);
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void accessToPongApiWithValidTokenShouldBePermitted() {
|
void accessToProtectedApiWithValidTokenShouldBePermitted() {
|
||||||
// given
|
// given
|
||||||
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void accessToPongApiWithInvalidTokenShouldBeDenied() {
|
void accessToProtectedApiWithInvalidTokenShouldBeDenied() {
|
||||||
// given
|
// given
|
||||||
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -190,7 +190,7 @@ class HsCredentialsControllerRestTest {
|
|||||||
given(rbacPersonRepo.findByUuid(personUuid)).willReturn(Optional.of(
|
given(rbacPersonRepo.findByUuid(personUuid)).willReturn(Optional.of(
|
||||||
HsOfficePersonRbacEntity.builder().uuid(personUuid).personType(NATURAL_PERSON).build()
|
HsOfficePersonRbacEntity.builder().uuid(personUuid).personType(NATURAL_PERSON).build()
|
||||||
));
|
));
|
||||||
given(rbacPersonRepo.findPersonsrepresentedByPersonWithUuid(personUuid)).willReturn(List.of(
|
given(rbacPersonRepo.findPersonsRepresentedByPersonWithUuid(personUuid)).willReturn(List.of(
|
||||||
// some persons, but not the one from the login-user itself
|
// some persons, but not the one from the login-user itself
|
||||||
HsOfficePersonRbacEntity.builder().uuid(UUID.randomUUID()).personType(NATURAL_PERSON).build(),
|
HsOfficePersonRbacEntity.builder().uuid(UUID.randomUUID()).personType(NATURAL_PERSON).build(),
|
||||||
HsOfficePersonRbacEntity.builder().uuid(UUID.randomUUID()).personType(LEGAL_PERSON).build()
|
HsOfficePersonRbacEntity.builder().uuid(UUID.randomUUID()).personType(LEGAL_PERSON).build()
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ import io.restassured.RestAssured;
|
|||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||||
|
import net.hostsharing.hsadminng.config.MessagesResourceConfig;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
|
||||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||||
@@ -36,7 +37,7 @@ import static org.hamcrest.Matchers.startsWith;
|
|||||||
@SpringBootTest(
|
@SpringBootTest(
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class,
|
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class,
|
||||||
MessageTranslator.class}
|
MessagesResourceConfig.class, MessageTranslator.class}
|
||||||
)
|
)
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|||||||
+2
-1
@@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.membership;
|
|||||||
|
|
||||||
import io.hypersistence.utils.hibernate.type.range.Range;
|
import io.hypersistence.utils.hibernate.type.range.Range;
|
||||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||||
|
import net.hostsharing.hsadminng.config.MessagesResourceConfig;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
|
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRbacEntity;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRbacEntity;
|
||||||
@@ -41,7 +42,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@WebMvcTest(HsOfficeMembershipController.class)
|
@WebMvcTest(HsOfficeMembershipController.class)
|
||||||
@Import({StrictMapper.class, DisableSecurityConfig.class, MessageTranslator.class})
|
@Import({ StrictMapper.class, DisableSecurityConfig.class, MessagesResourceConfig.class, MessageTranslator.class})
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
public class HsOfficeMembershipControllerRestTest {
|
public class HsOfficeMembershipControllerRestTest {
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -259,14 +259,14 @@ class HsOfficePersonRbacRepositoryIntegrationTest extends ContextBasedTestWithCl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findPersonsrepresentedByPersonWithUuid() {
|
public void findPersonsRepresentedByPersonWithUuid() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
context("superuser-alex@hostsharing.net");
|
context("superuser-alex@hostsharing.net");
|
||||||
final var personUuid = personRbacRepo.findPersonByOptionalNameLike("Fouler").getFirst().getUuid();
|
final var personUuid = personRbacRepo.findPersonByOptionalNameLike("Fouler").getFirst().getUuid();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@SuppressWarnings("unchecked") final List<HsOfficePersonRbacEntity> representedPersons = personRbacRepo.findPersonsrepresentedByPersonWithUuid(personUuid);
|
@SuppressWarnings("unchecked") final List<HsOfficePersonRbacEntity> representedPersons = personRbacRepo.findPersonsRepresentedByPersonWithUuid(personUuid);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(representedPersons).map(Object::toString).containsExactlyInAnyOrder(
|
assertThat(representedPersons).map(Object::toString).containsExactlyInAnyOrder(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.config.DisableSecurityConfig;
|
|||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||||
import org.junit.jupiter.api.Tag;
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.EnumSource;
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -40,28 +41,44 @@ class PingControllerAcceptanceTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
enum PingTranslationTestCase {
|
enum PongTranslationTestCase {
|
||||||
EN(Locale.ENGLISH, "pong superuser-alex@hostsharing.net - in English"),
|
EN(Locale.ENGLISH, "ponged superuser-alex@hostsharing.net - in English"),
|
||||||
DE(Locale.GERMAN, "pong superuser-alex@hostsharing.net - auf Deutsch"),
|
DE(Locale.GERMAN, "ponged superuser-alex@hostsharing.net - auf Deutsch");
|
||||||
FR(Locale.FRENCH, "pong superuser-alex@hostsharing.net - in English [fr translation missing]");
|
|
||||||
|
|
||||||
Locale givenLocale;
|
Locale givenLocale;
|
||||||
CharSequence expectedPongTranslation;
|
CharSequence expectedPongTranslation;
|
||||||
|
|
||||||
PingTranslationTestCase(final Locale givenLocale, final String expectedPongTranslation) {
|
PongTranslationTestCase(final Locale givenLocale, final String expectedPongTranslation) {
|
||||||
this.givenLocale = givenLocale;
|
this.givenLocale = givenLocale;
|
||||||
this.expectedPongTranslation = expectedPongTranslation;
|
this.expectedPongTranslation = expectedPongTranslation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@EnumSource(PingTranslationTestCase.class)
|
@EnumSource(PongTranslationTestCase.class)
|
||||||
void pingRepliesWithTranslatedPongResponse(final PingTranslationTestCase testCase) {
|
void pongRepliesWithTranslatedPongResponse(final PongTranslationTestCase testCase) {
|
||||||
final var responseBody = RestAssured // @formatter:off
|
final var responseBody = RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
||||||
.header("Accept-Language", testCase.givenLocale)
|
.header("Accept-Language", testCase.givenLocale)
|
||||||
.port(port)
|
.port(port)
|
||||||
|
.when()
|
||||||
|
.get("http://localhost/api/pong")
|
||||||
|
.then().log().all().assertThat()
|
||||||
|
.statusCode(200)
|
||||||
|
.contentType("text/plain;charset=UTF-8")
|
||||||
|
.extract().body().asString();
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
assertThat(responseBody).isEqualTo(testCase.expectedPongTranslation + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pingRepliesWithTranslatedPongResponse() {
|
||||||
|
final var responseBody = RestAssured // @formatter:off
|
||||||
|
.given()
|
||||||
|
.header("Accept-Language", Locale.GERMAN)
|
||||||
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.get("http://localhost/api/ping")
|
.get("http://localhost/api/ping")
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
@@ -70,6 +87,6 @@ class PingControllerAcceptanceTest {
|
|||||||
.extract().body().asString();
|
.extract().body().asString();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
assertThat(responseBody).isEqualTo(testCase.expectedPongTranslation + "\n");
|
assertThat(responseBody).isEqualTo("pinged - auf Deutsch\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ class PingControllerRestTest {
|
|||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
enum I18nTestCases {
|
enum I18nTestCases {
|
||||||
EN("en", "pong anonymousUser - in English"),
|
EN("en", "pinged - in English"),
|
||||||
DE("de", "pong anonymousUser - auf Deutsch");
|
DE("de", "pinged - auf Deutsch");
|
||||||
|
|
||||||
final String language;
|
final String language;
|
||||||
final String expectedTranslation;
|
final String expectedTranslation;
|
||||||
|
|||||||
Reference in New Issue
Block a user