add i18n support for CoopShareTx (#168)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/168 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
package net.hostsharing.hsadminng.hs.office.coopassets;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||||
import net.hostsharing.hsadminng.config.RetroactiveTranslator;
|
import net.hostsharing.hsadminng.config.RetroactiveTranslator;
|
||||||
@@ -8,7 +8,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
// HOWTO translate messages which got created without i18n support, in this case in a PostgreSQL constraint trigger
|
// HOWTO translate messages which got created without i18n support, in this case in a PostgreSQL constraint trigger
|
||||||
@Service
|
@Service
|
||||||
public class HsHostingAssetTranslations implements RetroactiveTranslator {
|
public class HsCoopAssetTranslations implements RetroactiveTranslator {
|
||||||
|
|
||||||
public static final String ERROR_400_PREFIX = "ERROR: [400] ";
|
public static final String ERROR_400_PREFIX = "ERROR: [400] ";
|
||||||
|
|
@@ -0,0 +1,26 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||||
|
import net.hostsharing.hsadminng.config.RetroactiveTranslator;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
// HOWTO translate messages which got created without i18n support, in this case in a PostgreSQL constraint trigger
|
||||||
|
@Service
|
||||||
|
public class HsCoopShareTranslations implements RetroactiveTranslator {
|
||||||
|
|
||||||
|
public static final String ERROR_400_PREFIX = "ERROR: [400] ";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageTranslator messageTranslator;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canTranslate(final String message) {
|
||||||
|
return message.equals("ERROR: [400] coop shares transaction would result in a negative number of shares");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translate(final String message) {
|
||||||
|
return ERROR_400_PREFIX + messageTranslator.translate(message.substring(ERROR_400_PREFIX.length()));
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
|||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
||||||
@@ -37,6 +38,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StrictMapper mapper;
|
private StrictMapper mapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageTranslator messageTranslator;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
|
||||||
|
|
||||||
@@ -118,32 +122,31 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
MultiValidationException.throwIfNotEmpty(violations);
|
MultiValidationException.throwIfNotEmpty(violations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateSubscriptionTransaction(
|
private void validateSubscriptionTransaction(
|
||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getTransactionType() == SUBSCRIPTION
|
if (requestBody.getTransactionType() == SUBSCRIPTION
|
||||||
&& requestBody.getShareCount() < 0) {
|
&& requestBody.getShareCount() < 0) {
|
||||||
violations.add("for %s, shareCount must be positive but is \"%d\"".formatted(
|
violations.add(messageTranslator.translate("for transactionType={0}, shareCount must be positive but is {1}",
|
||||||
requestBody.getTransactionType(), requestBody.getShareCount()));
|
requestBody.getTransactionType(), requestBody.getShareCount()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateCancellationTransaction(
|
private void validateCancellationTransaction(
|
||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getTransactionType() == CANCELLATION
|
if (requestBody.getTransactionType() == CANCELLATION
|
||||||
&& requestBody.getShareCount() > 0) {
|
&& requestBody.getShareCount() > 0) {
|
||||||
violations.add("for %s, shareCount must be negative but is \"%d\"".formatted(
|
violations.add(messageTranslator.translate("for transactionType={0}, shareCount must be negative but is {1}",
|
||||||
requestBody.getTransactionType(), requestBody.getShareCount()));
|
requestBody.getTransactionType(), requestBody.getShareCount()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateshareCount(
|
private void validateshareCount(
|
||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
final HsOfficeCoopSharesTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (requestBody.getShareCount() == 0) {
|
if (requestBody.getShareCount() == 0) {
|
||||||
violations.add("shareCount must not be 0 but is \"%d\"".formatted(
|
violations.add(messageTranslator.translate("shareCount must not be 0"));
|
||||||
requestBody.getShareCount()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,12 @@ unknown\ authorization\ ticket=unbekanntes Autorisierungs-Ticket
|
|||||||
{0}\ "{1}"\ not\ found\ or\ not\ accessible={0} "{1}" nicht gefunden oder nicht zugänglich
|
{0}\ "{1}"\ not\ found\ or\ not\ accessible={0} "{1}" nicht gefunden oder nicht zugänglich
|
||||||
but\ is=ist aber
|
but\ is=ist aber
|
||||||
|
|
||||||
|
# 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}
|
||||||
|
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-assets
|
# office.coop-assets
|
||||||
either\ {0}\ or\ {1}\ must\ be\ given=entweder {0} oder {1} muss angegeben werden
|
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
|
either\ {0}\ or\ {1}\ must\ be\ given,\ not\ both=entweder {0} oder {1} muss angegeben werden, nicht beide
|
||||||
|
@@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
|||||||
import io.restassured.RestAssured;
|
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.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;
|
||||||
@@ -32,7 +33,7 @@ import static org.hamcrest.Matchers.hasSize;
|
|||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
classes = {HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class})
|
classes = {HsadminNgApplication.class, DisableSecurityConfig.class, MessageTranslator.class, JpaAttempt.class})
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
@Transactional
|
@Transactional
|
||||||
@Tag("officeIntegrationTest")
|
@Tag("officeIntegrationTest")
|
||||||
@@ -180,7 +181,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class AddCoopSharesTransaction {
|
class PostNewCoopSharesTransaction {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canAddCoopSharesTransaction() {
|
void globalAdmin_canAddCoopSharesTransaction() {
|
||||||
@@ -191,6 +192,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
final var location = RestAssured // @formatter:off
|
final var location = RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
||||||
|
.header("Accept-Language", "de")
|
||||||
.contentType(ContentType.JSON).body("""
|
.contentType(ContentType.JSON).body("""
|
||||||
{
|
{
|
||||||
"membership.uuid": "%s",
|
"membership.uuid": "%s",
|
||||||
@@ -306,6 +308,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
||||||
|
.header("Accept-Language", "de")
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
@@ -329,7 +332,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
{
|
{
|
||||||
"statusCode": 400,
|
"statusCode": 400,
|
||||||
"statusPhrase": "Bad Request",
|
"statusPhrase": "Bad Request",
|
||||||
"message": "ERROR: [400] coop shares transaction would result in a negative number of shares"
|
"message": "ERROR: [400] Geschäftsanteile-Transaktion würde zu negativen Geschäftsanteilen führen"
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopshares;
|
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||||
|
|
||||||
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.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
@@ -27,7 +28,9 @@ 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(HsOfficeCoopSharesTransactionController.class)
|
@WebMvcTest(HsOfficeCoopSharesTransactionController.class)
|
||||||
@Import({DisableSecurityConfig.class, MessageTranslator.class})
|
@Import({DisableSecurityConfig.class,
|
||||||
|
MessagesResourceConfig.class,
|
||||||
|
MessageTranslator.class})
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
class HsOfficeCoopSharesTransactionControllerRestTest {
|
class HsOfficeCoopSharesTransactionControllerRestTest {
|
||||||
|
|
||||||
@@ -61,45 +64,45 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
|||||||
enum BadRequestTestCases {
|
enum BadRequestTestCases {
|
||||||
MEMBERSHIP_UUID_MISSING(
|
MEMBERSHIP_UUID_MISSING(
|
||||||
requestBody -> requestBody.without("membership.uuid"),
|
requestBody -> requestBody.without("membership.uuid"),
|
||||||
"membershipUuid must not be null"),
|
"membershipUuid darf nicht null sein ist aber null"),
|
||||||
|
|
||||||
TRANSACTION_TYPE_MISSING(
|
TRANSACTION_TYPE_MISSING(
|
||||||
requestBody -> requestBody.without("transactionType"),
|
requestBody -> requestBody.without("transactionType"),
|
||||||
"transactionType must not be null"),
|
"transactionType darf nicht null sein ist aber null"),
|
||||||
|
|
||||||
VALUE_DATE_MISSING(
|
VALUE_DATE_MISSING(
|
||||||
requestBody -> requestBody.without("valueDate"),
|
requestBody -> requestBody.without("valueDate"),
|
||||||
"valueDate must not be null"),
|
"valueDate darf nicht null sein ist aber null"),
|
||||||
|
|
||||||
SHARES_COUNT_FOR_SUBSCRIPTION_MUST_BE_POSITIVE(
|
SHARES_COUNT_FOR_SUBSCRIPTION_MUST_BE_POSITIVE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "SUBSCRIPTION")
|
.with("transactionType", "SUBSCRIPTION")
|
||||||
.with("shareCount", -1),
|
.with("shareCount", -1),
|
||||||
"for SUBSCRIPTION, shareCount must be positive but is \"-1\""),
|
"für transactionType=SUBSCRIPTION, muss shareCount positiv sein, ist aber -1"),
|
||||||
|
|
||||||
SHARES_COUNT_FOR_CANCELLATION_MUST_BE_NEGATIVE(
|
SHARES_COUNT_FOR_CANCELLATION_MUST_BE_NEGATIVE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "CANCELLATION")
|
.with("transactionType", "CANCELLATION")
|
||||||
.with("shareCount", 1),
|
.with("shareCount", 1),
|
||||||
"for CANCELLATION, shareCount must be negative but is \"1\""),
|
"für transactionType=CANCELLATION, muss shareCount negativ sein, ist aber 1"),
|
||||||
|
|
||||||
SHARES_COUNT_MUST_NOT_BE_NULL(
|
SHARES_COUNT_MUST_NOT_BE_NULL(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "REVERSAL")
|
.with("transactionType", "REVERSAL")
|
||||||
.with("shareCount", 0),
|
.with("shareCount", 0),
|
||||||
"shareCount must not be 0 but is \"0\""),
|
"shareCount darf nicht 0 sein"),
|
||||||
|
|
||||||
REFERENCE_MISSING(
|
REFERENCE_MISSING(
|
||||||
requestBody -> requestBody.without("reference"),
|
requestBody -> requestBody.without("reference"),
|
||||||
"reference must not be null"),
|
"reference darf nicht null sein ist aber null"),
|
||||||
|
|
||||||
REFERENCE_TOO_SHORT(
|
REFERENCE_TOO_SHORT(
|
||||||
requestBody -> requestBody.with("reference", "12345"),
|
requestBody -> requestBody.with("reference", "12345"),
|
||||||
"reference size must be between 6 and 48 but is \"12345\""),
|
"reference Größe muss zwischen 6 und 48 sein ist aber \"12345\""),
|
||||||
|
|
||||||
REFERENCE_TOO_LONG(
|
REFERENCE_TOO_LONG(
|
||||||
requestBody -> requestBody.with("reference", "0123456789012345678901234567890123456789012345678"),
|
requestBody -> requestBody.with("reference", "0123456789012345678901234567890123456789012345678"),
|
||||||
"reference size must be between 6 and 48 but is \"0123456789012345678901234567890123456789012345678\"");
|
"reference Größe muss zwischen 6 und 48 sein ist aber \"0123456789012345678901234567890123456789012345678\"");
|
||||||
|
|
||||||
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
|
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
|
||||||
private final String expectedErrorMessage;
|
private final String expectedErrorMessage;
|
||||||
@@ -124,6 +127,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
|||||||
mockMvc.perform(MockMvcRequestBuilders
|
mockMvc.perform(MockMvcRequestBuilders
|
||||||
.post("/api/hs/office/coopsharestransactions")
|
.post("/api/hs/office/coopsharestransactions")
|
||||||
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
.header("Authorization", "Bearer superuser-alex@hostsharing.net")
|
||||||
|
.header("Accept-Language", "de")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(testCase.givenRequestBody())
|
.content(testCase.givenRequestBody())
|
||||||
.accept(MediaType.APPLICATION_JSON))
|
.accept(MediaType.APPLICATION_JSON))
|
||||||
|
Reference in New Issue
Block a user