Skip to content

Commit 12c679d

Browse files
Merge pull request #1227 from folio-org/RMB-1047
RMB-1047: Support ) and = in error message fields
2 parents 492311c + 3a426f6 commit 12c679d

2 files changed

Lines changed: 120 additions & 28 deletions

File tree

domain-models-runtime/src/main/java/org/folio/rest/persist/PgUtil.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ public final class PgUtil {
8989
* <p>
9090
* 2 = <code>=(</code>
9191
*/
92+
@SuppressWarnings("java:S5852") // linear runtime thanks to atomic groups (?>)
9293
private static final Pattern KEY_ALREADY_EXISTS_PATTERN = Pattern.compile(
93-
"^Key \\(([^)]+)\\)=\\((.*)\\) already exists.$");
94+
"^Key (?>\\((.+)\\)=\\()(.*)\\) already exists.$");
9495
/**
9596
* Assume this String:
9697
* <p>
@@ -104,8 +105,9 @@ public final class PgUtil {
104105
* <p>
105106
* 3 = <code>users</code>
106107
*/
108+
@SuppressWarnings("java:S5852") // linear runtime thanks to atomic groups (?>)
107109
private static final Pattern KEY_NOT_PRESENT_PATTERN = Pattern.compile(
108-
"^Key [(]([^)]+)[)]=(?>[(](.*)[)]) is not present in table \"([^\"]+)\".$");
110+
"^Key (?>(?>[(](.+)[)]=[(])(.*)[)]) is not present in table \"([^\"]+)\".$");
109111
/**
110112
* Assume this String:
111113
* <p>
@@ -119,8 +121,9 @@ public final class PgUtil {
119121
* <p>
120122
* 3 = <code>referencing</code>
121123
*/
124+
@SuppressWarnings("java:S5852") // linear runtime thanks to atomic groups (?>)
122125
private static final Pattern KEY_STILL_REFERENCED_PATTERN = Pattern.compile(
123-
"^Key [(]([^)]+)[)]=(?>[(](.*)[)]) is still referenced from table \"([^\"]+)\".$");
126+
"^Key (?>(?>[(](.+)[)]=[(])(.*)[)]) is still referenced from table \"([^\"]+)\".$");
124127

125128
/** Number of records to read from the sort index in getWithOptimizedSql and generateOptimizedSql method */
126129
private static int optimizedSqlSize = 10000;

domain-models-runtime/src/test/java/org/folio/rest/persist/PgUtilIT.java

Lines changed: 114 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,50 +1070,139 @@ public void responseForeignKeyViolationNoMatch400(TestContext testContext) throw
10701070
}
10711071

10721072
@Test
1073-
public void responseForeignKeyViolationNoMatch422(TestContext testContext) throws Exception {
1074-
Exception genericDatabaseException = new PgException("", null, "", "bazMessage");
1075-
PgExceptionFacade exception = new PgExceptionFacade(genericDatabaseException);
1076-
Method respond400 = ResponseImpl.class.getMethod("respond400WithTextPlain", Object.class);
1077-
Method respond500 = ResponseImpl.class.getMethod("respond500WithTextPlain", Object.class);
1078-
Method respond422 = PgUtil.respond422method(ResponseWith422.class);
1079-
Future<Response> future = PgUtil.responseForeignKeyViolation("mytable", "myid", exception, respond422, respond400, respond500);
1073+
public void responseForeignKeyViolationNoMatch422() {
1074+
var msg = responseForeignKeyViolation("bazMessage");
1075+
assertThat(msg, containsString("bazMessage"));
1076+
}
1077+
1078+
@Test
1079+
@SuppressWarnings("java:S5976") // cannot use parameterized tests because we
1080+
// cannot combine @RunWith(VertxUnitRunner.class) and @RunWith(VertxUnitRunner.class)
1081+
public void keyNotPresentSimple() {
1082+
var msg = responseForeignKeyViolation("Key (name)=(x) is not present in table \"status\".");
1083+
assertThat(msg, is("Cannot set t.name = x because it does not exist in status.id."));
1084+
}
1085+
1086+
@Test
1087+
public void keyNotPresentComplex() {
1088+
var msg = responseForeignKeyViolation(
1089+
"Key (lower(jsonb ->> 'barcode'::text))=(foo(bar)baz) is not present in table \"status\".");
1090+
assertThat(msg, is(
1091+
"Cannot set t.lower(jsonb ->> 'barcode'::text) = foo(bar)baz because it does not exist in status.id."));
1092+
}
1093+
1094+
@Test
1095+
public void keyNotPresentComplex2() {
1096+
var msg = responseForeignKeyViolation(
1097+
"Key (CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END)=(foo(bar)baz) is not present in table \"status\".");
1098+
assertThat(msg, is("Cannot set t.CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END = foo(bar)baz "
1099+
+ "because it does not exist in status.id."));
1100+
}
1101+
1102+
@Test
1103+
public void keyStillReferencedSimple() {
1104+
var msg = responseForeignKeyViolation("Key (name)=(x) is still referenced from table \"status\".");
1105+
assertThat(msg, is("Cannot delete t.name = x because id is still referenced from table status."));
1106+
}
1107+
1108+
@Test
1109+
public void keyStillReferencedComplex() {
1110+
var msg = responseForeignKeyViolation(
1111+
"Key (lower(jsonb ->> 'barcode'::text))=(foo(bar)baz) is still referenced from table \"status\".");
1112+
assertThat(msg, is("Cannot delete t.lower(jsonb ->> 'barcode'::text) = foo(bar)baz "
1113+
+ "because id is still referenced from table status."));
1114+
}
1115+
1116+
@Test
1117+
public void keyStillReferencedComplex2() {
1118+
var msg = responseForeignKeyViolation("Key (CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END)=(foo(bar)baz) "
1119+
+ "is still referenced from table \"status\".");
1120+
assertThat(msg, is("Cannot delete t.CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END = foo(bar)baz "
1121+
+ "because id is still referenced from table status."));
1122+
}
1123+
1124+
private String responseForeignKeyViolation(String detail) {
1125+
var e = new PgException("errorMessage", "severity", "code", detail);
1126+
var future = responseForeignKeyViolation(true, new PgExceptionFacade(e));
10801127
assertTrue(future.succeeded());
10811128
assertThat(future.result().getStatus(), is(422));
1082-
Errors errors = (Errors) future.result().getEntity();
1083-
assertThat(errors.getErrors().get(0).getMessage(), containsString("bazMessage"));
1129+
var errors = (Errors) future.result().getEntity();
1130+
return errors.getErrors().get(0).getMessage();
1131+
}
1132+
1133+
private Future<Response> responseForeignKeyViolation(boolean with422method, PgExceptionFacade pgExceptionFacade) {
1134+
try {
1135+
var respond400 = ResponseImpl.class.getMethod("respond400WithTextPlain", Object.class);
1136+
var respond500 = ResponseImpl.class.getMethod("respond500WithTextPlain", Object.class);
1137+
var respond422 = with422method ? PgUtil.respond422method(ResponseWith422.class) : null;
1138+
return PgUtil.responseForeignKeyViolation("t", "myid", pgExceptionFacade, respond422, respond400, respond500);
1139+
} catch (NoSuchMethodException | SecurityException e) {
1140+
throw new RuntimeException(e);
1141+
}
10841142
}
10851143

10861144
@Test
1087-
public void responseUniqueViolationException(TestContext testContext) {
1145+
public void responseUniqueViolationException() {
10881146
Future<Response> future = PgUtil.responseUniqueViolation(null, null, null, null, null, null);
10891147
assertTrue(future.failed());
10901148
assertThat(future.cause(), is(instanceOf(NullPointerException.class)));
10911149
}
10921150

10931151
@Test
1094-
public void responseUniqueViolationNoMatch400(TestContext testContext) throws Exception {
1095-
Exception genericDatabaseException = new PgException("", null, "", "fooMessage");
1096-
PgExceptionFacade exception = new PgExceptionFacade(genericDatabaseException);
1097-
Method respond400 = ResponseImpl.class.getMethod("respond400WithTextPlain", Object.class);
1098-
Method respond500 = ResponseImpl.class.getMethod("respond500WithTextPlain", Object.class);
1099-
Future<Response> future = PgUtil.responseUniqueViolation("mytable", "myid", exception, null, respond400, respond500);
1152+
public void responseUniqueViolationNoMatch400() {
1153+
var e = new PgException("", null, "", "fooMessage");
1154+
Future<Response> future = responseUniqueViolation(false, new PgExceptionFacade(e));
11001155
assertTrue(future.succeeded());
11011156
assertThat(future.result().getStatus(), is(400));
11021157
assertThat(future.result().getEntity().toString(), containsString("fooMessage"));
11031158
}
11041159

11051160
@Test
1106-
public void responseUniqueViolationNoMatch422(TestContext testContext) throws Exception {
1107-
Exception genericDatabaseException = new PgException("", null, "", "fooMessage");
1108-
PgExceptionFacade exception = new PgExceptionFacade(genericDatabaseException);
1109-
Method respond400 = ResponseImpl.class.getMethod("respond400WithTextPlain", Object.class);
1110-
Method respond500 = ResponseImpl.class.getMethod("respond500WithTextPlain", Object.class);
1111-
Method respond422 = PgUtil.respond422method(ResponseWith422.class);
1112-
Future<Response> future = PgUtil.responseUniqueViolation("mytable", "myid", exception, respond422, respond400, respond500);
1161+
public void responseUniqueViolationNoMatch() {
1162+
var msg = responseUniqueViolation("fooMessage");
1163+
assertThat(msg, containsString("fooMessage"));
1164+
}
1165+
1166+
@Test
1167+
@SuppressWarnings("java:S5976") // cannot use parameterized tests because we
1168+
// cannot combine @RunWith(VertxUnitRunner.class) and @RunWith(VertxUnitRunner.class)
1169+
public void responseUniqueViolationSimple() {
1170+
var msg = responseUniqueViolation("Key (barcode)=(12345) already exists.");
1171+
assertThat(msg, is("barcode value already exists in table mytable: 12345"));
1172+
}
1173+
1174+
@Test
1175+
public void responseUniqueViolationComplex() {
1176+
var msg = responseUniqueViolation("Key (lower(jsonb ->> 'barcode'::text))=(foo(bar)baz) already exists.");
1177+
assertThat(msg, is("lower(jsonb ->> 'barcode'::text) value already exists in table mytable: foo(bar)baz"));
1178+
}
1179+
1180+
@Test
1181+
public void responseUniqueViolationComplex2() {
1182+
var msg = responseUniqueViolation(
1183+
"Key (CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END)=(foo(bar)baz) already exists.");
1184+
assertThat(msg, is(
1185+
"CASE WHEN barcode='nil' THEN 'n/a' ELSE barcode END value already exists in table mytable: foo(bar)baz"));
1186+
}
1187+
1188+
private String responseUniqueViolation(String detail) {
1189+
var e = new PgException("errorMessage", "severity", "code", detail);
1190+
var future = responseUniqueViolation(true, new PgExceptionFacade(e));
11131191
assertTrue(future.succeeded());
11141192
assertThat(future.result().getStatus(), is(422));
1115-
Errors errors = (Errors) future.result().getEntity();
1116-
assertThat(errors.getErrors().get(0).getMessage(), containsString("fooMessage"));
1193+
var errors = (Errors) future.result().getEntity();
1194+
return errors.getErrors().get(0).getMessage();
1195+
}
1196+
1197+
private Future<Response> responseUniqueViolation(boolean with422method, PgExceptionFacade pgExceptionFacade) {
1198+
try {
1199+
var respond400 = ResponseImpl.class.getMethod("respond400WithTextPlain", Object.class);
1200+
var respond500 = ResponseImpl.class.getMethod("respond500WithTextPlain", Object.class);
1201+
var respond422 = with422method ? PgUtil.respond422method(ResponseWith422.class) : null;
1202+
return PgUtil.responseUniqueViolation("mytable", "myid", pgExceptionFacade, respond422, respond400, respond500);
1203+
} catch (NoSuchMethodException | SecurityException e) {
1204+
throw new RuntimeException(e);
1205+
}
11171206
}
11181207

11191208
@Test(expected = NoSuchMethodException.class)

0 commit comments

Comments
 (0)