Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/translation-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ Never persist both forms.
of the commarea (e.g. `INQCUST-CUSTNO` → `customerNumber`).
- Validation via `jakarta.validation` annotations; constraint violations
yield HTTP 400 from the `@ControllerAdvice`.
- Required fields on request-DTO records are expressed exclusively through
`@NotNull` (and `@NotBlank` for non-blank strings such as customer
name/address) on the record components, with `@Valid` on the controller
parameter and on every nested DTO/Key reference. Do **not** put
`Objects.requireNonNull(...)` checks inside the canonical constructor of
a request DTO: those throw `NullPointerException` during Jackson
deserialization, bypass `MethodArgumentNotValidException`, and surface
as a 500 (`UNEX`) instead of the intended 400. Issues #10 and #15 are
the empirical source for this rule.
- A "not found" commarea result (e.g. `INQCUST-INQ-FAIL-CD = '1'`) maps to
HTTP 404 with a `{ failCode, message }` body, **not** an exception.
- Hard failures (commarea-style abend) map to HTTP 500 with a structured
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/augment/cbsa/service/CrecustService.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class CrecustService {
private static final int REVIEW_DATE_BOUND = 20;
private static final long CREDIT_AGENCY_REPLY_WINDOW_SECONDS = 3;
private static final List<String> VALID_TITLES = List.of(
"Professor", "Mr", "Mrs", "Miss", "Ms", "Dr", "Drs", "Lord", "Sir", "Lady", ""
"Professor", "Mr", "Mrs", "Miss", "Ms", "Dr", "Drs", "Lord", "Sir", "Lady"
);

private final CrecustRepository crecustRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.Objects;

public record CreaccCommareaRequestDto(
@JsonProperty("CommEyecatcher")
Expand Down Expand Up @@ -83,14 +82,4 @@ public record CreaccCommareaRequestDto(
@Size(max = 1)
String commFailCode
) {

public CreaccCommareaRequestDto {
Objects.requireNonNull(commCustno, "commCustno must not be null");
Objects.requireNonNull(commKey, "commKey must not be null");
Objects.requireNonNull(commAccType, "commAccType must not be null");
Objects.requireNonNull(commIntRt, "commIntRt must not be null");
Objects.requireNonNull(commOverdrLim, "commOverdrLim must not be null");
Objects.requireNonNull(commAvailBal, "commAvailBal must not be null");
Objects.requireNonNull(commActBal, "commActBal must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.Objects;

public record CreaccKeyDto(
@JsonProperty("CommSortcode")
Expand All @@ -19,9 +18,4 @@ public record CreaccKeyDto(
@Max(99_999_999)
Long commNumber
) {

public CreaccKeyDto {
Objects.requireNonNull(commSortcode, "commSortcode must not be null");
Objects.requireNonNull(commNumber, "commNumber must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;

public record CreaccRequestDto(
@JsonProperty("CreAcc")
@Valid
@NotNull
CreaccCommareaRequestDto creAcc
) {

public CreaccRequestDto {
Objects.requireNonNull(creAcc, "creAcc must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.Objects;

public record CrecustCommareaRequestDto(
@JsonProperty("CommEyecatcher")
Expand All @@ -19,7 +19,7 @@ public record CrecustCommareaRequestDto(
CrecustKeyDto commKey,

@JsonProperty("CommName")
@NotNull
@NotBlank
@Size(max = 60)
String commName,

Expand Down Expand Up @@ -52,11 +52,4 @@ public record CrecustCommareaRequestDto(
@Size(max = 1)
String commFailCode
) {

public CrecustCommareaRequestDto {
Objects.requireNonNull(commKey, "commKey must not be null");
Objects.requireNonNull(commName, "commName must not be null");
Objects.requireNonNull(commAddress, "commAddress must not be null");
Objects.requireNonNull(commDateOfBirth, "commDateOfBirth must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.Objects;

public record CrecustKeyDto(
@JsonProperty("CommSortcode")
Expand All @@ -19,9 +18,4 @@ public record CrecustKeyDto(
@Max(9_999_999_999L)
Long commNumber
) {

public CrecustKeyDto {
Objects.requireNonNull(commSortcode, "commSortcode must not be null");
Objects.requireNonNull(commNumber, "commNumber must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;

public record CrecustRequestDto(
@JsonProperty("CreCust")
@Valid
@NotNull
CrecustCommareaRequestDto creCust
) {

public CrecustRequestDto {
Objects.requireNonNull(creCust, "creCust must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;

public record DbcrfunRequestDto(
@JsonProperty("PAYDBCR")
@Valid
@NotNull
DbcrfunCommareaRequestDto paydbcr
) {

public DbcrfunRequestDto {
Objects.requireNonNull(paydbcr, "paydbcr must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;

public record DelcusRequestDto(
@JsonProperty("DelCus")
@Valid
@NotNull
DelcusCommareaRequestDto delCus
) {

public DelcusRequestDto {
Objects.requireNonNull(delCus, "delCus must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.util.Objects;

public record UpdcustCommareaRequestDto(
@JsonProperty("CommEye")
Expand All @@ -24,12 +24,12 @@ public record UpdcustCommareaRequestDto(
String commCustno,

@JsonProperty("CommName")
@NotNull
@NotBlank
@Size(max = 60)
String commName,

@JsonProperty("CommAddress")
@NotNull
@NotBlank
@Size(max = 160)
String commAddress,

Expand Down Expand Up @@ -59,14 +59,4 @@ public record UpdcustCommareaRequestDto(
@Size(max = 1)
String commUpdFailCd
) {

public UpdcustCommareaRequestDto {
Objects.requireNonNull(commScode, "commScode must not be null");
Objects.requireNonNull(commCustno, "commCustno must not be null");
Objects.requireNonNull(commName, "commName must not be null");
Objects.requireNonNull(commAddress, "commAddress must not be null");
Objects.requireNonNull(commDob, "commDob must not be null");
Objects.requireNonNull(commCreditScore, "commCreditScore must not be null");
Objects.requireNonNull(commCsReviewDate, "commCsReviewDate must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;

public record UpdcustRequestDto(
@JsonProperty("UpdCust")
@Valid
@NotNull
UpdcustCommareaRequestDto updCust
) {

public UpdcustRequestDto {
Objects.requireNonNull(updCust, "updCust must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ void rejectsInvalidCustomerTitles() {
verifyNoInteractions(creditAgencyService, crecustRepository, reviewDateRandom);
}

@Test
void rejectsBlankCustomerNamesAtTitleValidation() {
CrecustResult result = crecustService.create(new CrecustRequest(" ", "1 Main Street", 1012000));

assertThat(result.creationSuccess()).isFalse();
assertThat(result.failCode()).isEqualTo("T");
verifyNoInteractions(creditAgencyService, crecustRepository, reviewDateRandom);
}

@Test
void rejectsDatesEarlierThan1601() {
CrecustResult result = crecustService.create(new CrecustRequest("Dr Example", "1 Main Street", 1011500));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ void requestValidationFailuresRemainProblemDetails() throws Exception {
.andExpect(jsonPath("$.title").value("Validation failed"));
}

@Test
void missingRequiredFieldReturnsBadRequest() throws Exception {
// CommName omitted entirely; bean validation must reject with 400 rather
// than letting a constructor NPE escalate to a 500 abend.
mockMvc.perform(post("/api/v1/crecust/insert")
.contentType(APPLICATION_JSON)
.content("""
{"CreCust":{"CommKey":{"CommSortcode":"000000","CommNumber":0},"CommAddress":"1 Main Street","CommDateOfBirth":10012000}}
"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.title").value("Validation failed"));
}

@Test
void blankCommNameReturnsBadRequest() throws Exception {
mockMvc.perform(post("/api/v1/crecust/insert")
.contentType(APPLICATION_JSON)
.content(requestJson(" ", "1 Main Street", 10_01_2000)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.title").value("Validation failed"));
}

@Test
void redactsAbendExceptionMessageFromResponseBody() throws Exception {
when(crecustService.create(new CrecustRequest("Dr Alice Example", "1 Main Street", 10_01_2000)))
Expand Down
Loading