diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 520335fca6b..5e8b7d27de7 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index a0e76657589..c12e6e57b77 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 5fb7d9b771b..fb35d780562 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index fae7bf1c491..8cf10972757 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -155,6 +155,7 @@ ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder.invalidCodeMissin ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.matchesFound=Matches found ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.noMatchesFound=No Matches found +ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.onlyNegativeMatchesFound=Only negative matches found ca.uhn.fhir.jpa.dao.JpaResourceDaoSearchParameter.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1} diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 37459003bbd..4da0f9c1d40 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 659f30ce0fc..08e0b9c2eaf 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index d5a85e8db5e..eed137d374d 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommandR4Test.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommandR4Test.java index ed1aecab23f..ce90bfd1853 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommandR4Test.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommandR4Test.java @@ -93,7 +93,8 @@ public class ExportConceptMapToCsvCommandR4Test { "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2a\",\"Display 2a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" + "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2b\",\"Display 2b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" + "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2c\",\"Display 2c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" + - "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n"; + "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n" + + "\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2e\",\"Display 2e\",\"\",\"\",\"unmatched\",\"3e This is a comment.\"\n"; String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8); assertEquals(expected, result); @@ -272,6 +273,16 @@ public class ExportConceptMapToCsvCommandR4Test { .setEquivalence(ConceptMapEquivalence.EQUAL) .setComment("3d This is a comment."); + element = group.addElement(); + element + .setCode("Code 2e") + .setDisplay("Display 2e"); + + target = element.addTarget(); + target + .setEquivalence(ConceptMapEquivalence.UNMATCHED) + .setComment("3e This is a comment."); + return conceptMap; } } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandDstu3Test.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandDstu3Test.java index 8f26db7f01d..bf6482c9f80 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandDstu3Test.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandDstu3Test.java @@ -273,7 +273,7 @@ public class ImportCsvToConceptMapCommandDstu3Test { assertEquals(CS_URL_3, group.getTarget()); assertEquals("Version 3t", group.getTargetVersion()); - assertEquals(4, group.getElement().size()); + assertEquals(5, group.getElement().size()); source = group.getElement().get(0); assertEquals("Code 2a", source.getCode()); @@ -323,6 +323,19 @@ public class ImportCsvToConceptMapCommandDstu3Test { assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); assertEquals("3d This is a comment.", target.getComment()); + // ensure unmatched codes are handled correctly + source = group.getElement().get(4); + assertEquals("Code 2e", source.getCode()); + assertEquals("Display 2e", source.getDisplay()); + + assertEquals(1, source.getTarget().size()); + + target = source.getTarget().get(0); + assertNull(target.getCode()); + assertNull(target.getDisplay()); + assertEquals(ConceptMapEquivalence.UNMATCHED, target.getEquivalence()); + assertEquals("3e This is a comment.", target.getComment()); + App.main(myTlsAuthenticationTestHelper.createBaseRequestGeneratingCommandArgs( new String[]{ ImportCsvToConceptMapCommand.COMMAND, diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandR4Test.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandR4Test.java index 4b36f88f200..74097d9e343 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandR4Test.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommandR4Test.java @@ -284,7 +284,7 @@ public class ImportCsvToConceptMapCommandR4Test { assertEquals(CS_URL_3, group.getTarget()); assertEquals("Version 3t", group.getTargetVersion()); - assertEquals(4, group.getElement().size()); + assertEquals(5, group.getElement().size()); source = group.getElement().get(0); assertEquals("Code 2a", source.getCode()); @@ -334,6 +334,19 @@ public class ImportCsvToConceptMapCommandR4Test { assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); assertEquals("3d This is a comment.", target.getComment()); + // ensure unmatched codes are handled correctly + source = group.getElement().get(4); + assertEquals("Code 2e", source.getCode()); + assertEquals("Display 2e", source.getDisplay()); + + assertEquals(1, source.getTarget().size()); + + target = source.getTarget().get(0); + assertNull(target.getCode()); + assertNull(target.getDisplay()); + assertEquals(ConceptMapEquivalence.UNMATCHED, target.getEquivalence()); + assertEquals("3e This is a comment.", target.getComment()); + App.main(myTlsAuthenticationTestHelper.createBaseRequestGeneratingCommandArgs( new String[]{ ImportCsvToConceptMapCommand.COMMAND, diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/import-csv-to-conceptmap-command-test-input.csv b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/import-csv-to-conceptmap-command-test-input.csv index 972015c13b8..7c2637c5237 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/import-csv-to-conceptmap-command-test-input.csv +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/import-csv-to-conceptmap-command-test-input.csv @@ -11,3 +11,4 @@ "http://example.com/codesystem/2","Version 2s","http://example.com/codesystem/3","Version 3t","Code 2b","Display 2b","Code 3b","Display 3b","equal","3b This is a comment." "http://example.com/codesystem/2","Version 2s","http://example.com/codesystem/3","Version 3t","Code 2c","Display 2c","Code 3c","Display 3c","equal","3c This is a comment." "http://example.com/codesystem/2","Version 2s","http://example.com/codesystem/3","Version 3t","Code 2d","Display 2d","Code 3d","Display 3d","equal","3d This is a comment." +"http://example.com/codesystem/2","Version 2s","http://example.com/codesystem/3","Version 3t","Code 2e","Display 2e","","","unmatched","3e This is a comment." diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 31efb5e1cdc..de8fa790b0b 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index bae9d20464c..4fc7c8e427e 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 0dce45e5b92..ce20d1ab6b2 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 0c8fd1a7c43..638f1ed7be0 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 69c806cf9fb..fca0987ddf5 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 5463e9b5d5a..d90951583dd 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index b253184ccbf..52d01b8dfbf 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5801-add-_language-param.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5801-add-_language-param.yaml new file mode 100644 index 00000000000..b8ed8d41f51 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5801-add-_language-param.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5801 +title: "Support for the _language parameter was added but it was not able to be used by clients of a JPA server +because the _language parameter was not added to the resource providers. Additionally, no error message +was returned when language support was disabled and a search with _language was performed. This has +been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5814-resource-provider-factory.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5814-resource-provider-factory.yaml new file mode 100644 index 00000000000..9667dc0e6e2 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5814-resource-provider-factory.yaml @@ -0,0 +1,5 @@ +--- +type: change +issue: 5814 +title: "Extracted methods out of ResourceProviderFactory into ObservableSupplierSet so that functionality can be used by +other services. Unit tests revealed a cleanup bug in MdmProviderLoader that is fixed in this MR." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5816-unmatched-translate-target.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5816-unmatched-translate-target.yaml new file mode 100644 index 00000000000..2c3f467b81a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5816-unmatched-translate-target.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5816 +title: "The ConceptMap/$translate operation will include targets with an equivalence code of `unmatched` in the response +regardless of whether the target has a code." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5817-remove-forced-id.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5817-remove-forced-id.yaml new file mode 100644 index 00000000000..db7288aa991 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5817-remove-forced-id.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5817 +title: "The HFJ_FORCED_ID table is no longer used." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5820-unifying-patient-type-and-instance-bulk-exports.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5820-unifying-patient-type-and-instance-bulk-exports.yaml new file mode 100644 index 00000000000..fd97471bc53 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5820-unifying-patient-type-and-instance-bulk-exports.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5820 +title: "Unifying the code paths for Patient type export and Patient instance export. + These paths should be the same, since type is defined by spec, but instance + is just 'syntactic sugar' on top of that spec (and so should be the same). +" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5824-fix-history-prefetch.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5824-fix-history-prefetch.yaml new file mode 100644 index 00000000000..f1a9dd7f3be --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5824-fix-history-prefetch.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5824 +jira: SMILE-7999 +title: "We now avoid a query during reindex and transaction processing that was very slow on Sql Server." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5828-reduction-jobs-failing-in-reduction-fails-jobs.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5828-reduction-jobs-failing-in-reduction-fails-jobs.yaml new file mode 100644 index 00000000000..978d0e3e233 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5828-reduction-jobs-failing-in-reduction-fails-jobs.yaml @@ -0,0 +1,8 @@ +--- +type: fix +issue: 5828 +title: "When batch 2 jobs with Reduction steps fail in the final part + of the reduction step, this would often leave the job + stuck in the FINALIZE state. + This has been fixed; the job will now FAIL. +" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/upgrade.md index e69de29bb2d..a7b6aa33ff3 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/upgrade.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/upgrade.md @@ -0,0 +1,4 @@ + +## HFJ_FORCED_ID Table +The HFJ_FORCED_ID table is no longer used. +Users may delete it after upgrading to 7.2 to free database storage space. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md index f4fbcbce057..c9000f3670c 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md @@ -84,6 +84,15 @@ The HFJ_RESOURCE table indicates a single resource of any type in the database. Contains the resource type (e.g. Patient) + + FHIR_ID + + String + + + Contains the FHIR Resource id element. Either the PID, or the client-assigned id. + + HASH_SHA256 @@ -243,30 +252,6 @@ The complete raw contents of the resource is stored in either the `RES_TEXT` or - - -# HFJ_FORCED_ID: Client Assigned/Visible Resource IDs - -By default, the **HFJ_RESOURCE.RES_ID** column is used as the resource ID for all server-assigned IDs. For example, if a Patient resource is created in a completely empty database, it will be assigned the ID `Patient/1` by the server and RES_ID will have a value of 1. - -However, when client-assigned IDs are used, these may contain text values to allow a client to create an ID such -as `Patient/ABC`. When a client-assigned ID is given to a resource, a row is created in the **HFJ_RESOURCE** table. When -an **HFJ_FORCED_ID** row exists corresponding to the equivalent **HFJ_RESOURCE** row, the RES_ID value is no longer -visible or usable by FHIR clients and it becomes purely an internal ID to the JPA server. - -If the server has been configured with -a [Resource Server ID Strategy](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.html#setResourceServerIdStrategy(ca.uhn.fhir.jpa.api.config.JpaStorageSettings.IdStrategyEnum)) -of [UUID](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.IdStrategyEnum.html#UUID), or the -server has been configured with -a [Resource Client ID Strategy](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.html#setResourceClientIdStrategy(ca.uhn.fhir.jpa.api.config.JpaStorageSettings.ClientIdStrategyEnum)) -of [ANY](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.ClientIdStrategyEnum.html#ANY) -the server will create a Forced ID for all resources (not only resources having textual IDs). - -The **HFJ_RESOURCE** table now has a FHIR_ID column. -This column is always populated; both for server-assigned ids and for client-assigned ids. -As of Hapi release 6.10, this column is used in place of the **HFJ_FORCED_ID** table for id search and sort. -A future version of Hapi will stop populating the **HFJ_FORCED_ID** table. - ## Columns diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index a5d73a949a7..e7c060a760f 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index c992c31b9bb..e00c808a8c9 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index dc0e37591be..a530d5d127d 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 9e57f2dedae..3e92c57b452 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 06200fc852a..ebf6936a6f4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -37,7 +37,6 @@ import ca.uhn.fhir.jpa.api.dao.IJpaDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; @@ -195,9 +194,6 @@ public abstract class BaseHapiFhirDao extends BaseStora @Autowired protected IIdHelperService myIdHelperService; - @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired protected ISearchCoordinatorSvc mySearchCoordinatorSvc; @@ -1204,10 +1200,6 @@ public abstract class BaseHapiFhirDao extends BaseStora if (entity.getId() == null) { myEntityManager.persist(entity); - if (entity.getForcedId() != null) { - myEntityManager.persist(entity.getForcedId()); - } - postPersist(entity, (T) theResource, theRequest); } else if (entity.getDeleted() != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index b86c9e2cb53..686667b2813 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -44,14 +44,12 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOptions; import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; import ca.uhn.fhir.jpa.api.model.LazyDaoMethodOutcome; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.delete.DeleteConflictUtil; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.BaseHasResource; import ca.uhn.fhir.jpa.model.entity.BaseTag; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; @@ -517,18 +515,12 @@ public abstract class BaseHapiFhirResourceDao extends B } } - String resourceIdBeforeStorage = theResource.getIdElement().getIdPart(); - boolean resourceHadIdBeforeStorage = isNotBlank(resourceIdBeforeStorage); - boolean resourceIdWasServerAssigned = - theResource.getUserData(JpaConstants.RESOURCE_ID_SERVER_ASSIGNED) == Boolean.TRUE; - if (resourceHadIdBeforeStorage) { - entity.setFhirId(resourceIdBeforeStorage); - } + boolean isClientAssignedId = storeNonPidResourceId(theResource, entity); HookParams hookParams; // Notify interceptor for accepting/rejecting client assigned ids - if (!resourceIdWasServerAssigned && resourceHadIdBeforeStorage) { + if (isClientAssignedId) { hookParams = new HookParams().add(IBaseResource.class, theResource).add(RequestDetails.class, theRequest); doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRESTORAGE_CLIENT_ASSIGNED_ID, hookParams); } @@ -542,7 +534,7 @@ public abstract class BaseHapiFhirResourceDao extends B .add(TransactionDetails.class, theTransactionDetails); doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, hookParams); - if (resourceHadIdBeforeStorage && !resourceIdWasServerAssigned) { + if (isClientAssignedId) { validateResourceIdCreation(theResource, theRequest); } @@ -569,31 +561,6 @@ public abstract class BaseHapiFhirResourceDao extends B // Store the resource forced ID if necessary JpaPid jpaPid = JpaPid.fromId(updatedEntity.getResourceId()); - if (resourceHadIdBeforeStorage) { - if (resourceIdWasServerAssigned) { - boolean createForPureNumericIds = true; - createForcedIdIfNeeded(entity, resourceIdBeforeStorage, createForPureNumericIds); - } else { - boolean createForPureNumericIds = getStorageSettings().getResourceClientIdStrategy() - != JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC; - createForcedIdIfNeeded(entity, resourceIdBeforeStorage, createForPureNumericIds); - } - } else { - switch (getStorageSettings().getResourceClientIdStrategy()) { - case NOT_ALLOWED: - case ALPHANUMERIC: - break; - case ANY: - boolean createForPureNumericIds = true; - createForcedIdIfNeeded( - updatedEntity, theResource.getIdElement().getIdPart(), createForPureNumericIds); - // for client ID mode ANY, we will always have a forced ID. If we ever - // stop populating the transient forced ID be warned that we use it - // (and expect it to be set correctly) farther below. - assert updatedEntity.getTransientForcedId() != null; - break; - } - } // Populate the resource with its actual final stored ID from the entity theResource.setId(entity.getIdDt()); @@ -601,7 +568,7 @@ public abstract class BaseHapiFhirResourceDao extends B // Pre-cache the resource ID jpaPid.setAssociatedResourceId(entity.getIdType(myFhirContext)); myIdHelperService.addResolvedPidToForcedId( - jpaPid, theRequestPartitionId, getResourceName(), entity.getTransientForcedId(), null); + jpaPid, theRequestPartitionId, getResourceName(), entity.getFhirId(), null); theTransactionDetails.addResolvedResourceId(jpaPid.getAssociatedResourceId(), jpaPid); theTransactionDetails.addResolvedResource(jpaPid.getAssociatedResourceId(), theResource); @@ -646,40 +613,34 @@ public abstract class BaseHapiFhirResourceDao extends B return outcome; } - private void createForcedIdIfNeeded( - ResourceTable theEntity, String theResourceId, boolean theCreateForPureNumericIds) { - // TODO MB delete this in step 3 - if (isNotBlank(theResourceId) && theEntity.getForcedId() == null) { - if (theCreateForPureNumericIds || !IdHelperService.isValidPid(theResourceId)) { - ForcedId forcedId = new ForcedId(); - forcedId.setResourceType(theEntity.getResourceType()); - forcedId.setForcedId(theResourceId); - forcedId.setResource(theEntity); - forcedId.setPartitionId(theEntity.getPartitionId()); + /** + * Check for an id on the resource and if so, + * store it in ResourceTable. + * + * The fhirId property is either set here with the resource id + * OR by hibernate once the PK is generated for a server-assigned id. + * + * Used for both client-assigned id and for server-assigned UUIDs. + * + * @return true if this is a client-assigned id + * + * @see ca.uhn.fhir.jpa.model.entity.ResourceTable.FhirIdGenerator + */ + private boolean storeNonPidResourceId(T theResource, ResourceTable entity) { + String resourceIdBeforeStorage = theResource.getIdElement().getIdPart(); + boolean resourceHadIdBeforeStorage = isNotBlank(resourceIdBeforeStorage); + boolean resourceIdWasServerAssigned = + theResource.getUserData(JpaConstants.RESOURCE_ID_SERVER_ASSIGNED) == Boolean.TRUE; - /* - * As of Hibernate 5.6.2, assigning the forced ID to the - * resource table causes an extra update to happen, even - * though the ResourceTable entity isn't actually changed - * (there is a @OneToOne reference on ResourceTable to the - * ForcedId table, but the actual column is on the ForcedId - * table so it doesn't actually make sense to update the table - * when this is set). But to work around that we avoid - * actually assigning ResourceTable#myForcedId here. - * - * It's conceivable they may fix this in the future, or - * they may not. - * - * If you want to try assigning the forced it to the resource - * entity (by calling ResourceTable#setForcedId) try running - * the tests FhirResourceDaoR4QueryCountTest to verify that - * nothing has broken as a result. - * JA 20220121 - */ - theEntity.setTransientForcedId(forcedId.getForcedId()); - myForcedIdDao.save(forcedId); - } + // We distinguish actual client-assigned ids from UUIDs which the server assigned. + boolean isClientAssigned = resourceHadIdBeforeStorage && !resourceIdWasServerAssigned; + + // But both need to be set on the entity fhirId field. + if (resourceHadIdBeforeStorage) { + entity.setFhirId(resourceIdBeforeStorage); } + + return isClientAssigned; } void validateResourceIdCreation(T theResource, RequestDetails theRequest) { @@ -1891,7 +1852,6 @@ public abstract class BaseHapiFhirResourceDao extends B throw new ResourceNotFoundException(Msg.code(1998) + theId); } validateGivenIdIsAppropriateToRetrieveResource(theId, entity); - entity.setTransientForcedId(theId.getIdPart()); return entity; } @@ -2634,18 +2594,14 @@ public abstract class BaseHapiFhirResourceDao extends B } private void validateGivenIdIsAppropriateToRetrieveResource(IIdType theId, BaseHasResource entity) { - if (entity.getForcedId() != null) { - if (getStorageSettings().getResourceClientIdStrategy() != JpaStorageSettings.ClientIdStrategyEnum.ANY) { - if (theId.isIdPartValidLong()) { - // This means that the resource with the given numeric ID exists, but it has a "forced ID", meaning - // that - // as far as the outside world is concerned, the given ID doesn't exist (it's just an internal - // pointer - // to the - // forced ID) - throw new ResourceNotFoundException(Msg.code(2000) + theId); - } - } + if (!entity.getIdDt().getIdPart().equals(theId.getIdPart())) { + // This means that the resource with the given numeric ID exists, but it has a "forced ID", meaning + // that + // as far as the outside world is concerned, the given ID doesn't exist (it's just an internal + // pointer + // to the + // forced ID) + throw new ResourceNotFoundException(Msg.code(2000) + theId); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index 3875153f138..e1f7749801a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -28,7 +28,6 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; -import ca.uhn.fhir.jpa.dao.data.IResourceTagDao; import ca.uhn.fhir.jpa.dao.expunge.ExpungeService; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; @@ -38,7 +37,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory; -import ca.uhn.fhir.jpa.search.builder.SearchBuilder; +import ca.uhn.fhir.jpa.search.SearchConstants; import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -47,34 +46,31 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.JoinType; -import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Root; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; public abstract class BaseHapiFhirSystemDao extends BaseStorageDao implements IFhirSystemDao { - - public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0]; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class); + public ResourceCountCache myResourceCountsCache; @PersistenceContext(type = PersistenceContextType.TRANSACTION) @@ -95,9 +91,6 @@ public abstract class BaseHapiFhirSystemDao extends B @Autowired private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory; - @Autowired - private IResourceTagDao myResourceTagDao; - @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -181,13 +174,25 @@ public abstract class BaseHapiFhirSystemDao extends B return myTransactionProcessor.transaction(theRequestDetails, theRequest, true); } + /** + * Prefetch entities into the Hibernate session. + * + * When processing several resources (e.g. transaction bundle, $reindex chunk, etc.) + * it would be slow to fetch each piece of a resource (e.g. all token index rows) + * one resource at a time. + * Instead, we fetch all the linked resources for the entire batch and populate the Hibernate Session. + * + * @param theResolvedIds the pids + * @param thePreFetchIndexes Should resource indexes be loaded + */ + @SuppressWarnings("rawtypes") @Override public

void preFetchResources( List

theResolvedIds, boolean thePreFetchIndexes) { HapiTransactionService.requireTransaction(); List pids = theResolvedIds.stream().map(t -> ((JpaPid) t).getId()).collect(Collectors.toList()); - new QueryChunker().chunk(pids, ids -> { + new QueryChunker().chunk(pids, idChunk -> { /* * Pre-fetch the resources we're touching in this transaction in mass - this reduced the @@ -200,117 +205,110 @@ public abstract class BaseHapiFhirSystemDao extends B * * However, for realistic average workloads, this should reduce the number of round trips. */ - if (ids.size() >= 2) { - List loadedResourceTableEntries = new ArrayList<>(); - preFetchIndexes(ids, "forcedId", "myForcedId", loadedResourceTableEntries); - - List entityIds; + if (idChunk.size() >= 2) { + List entityChunk = prefetchResourceTableHistoryAndProvenance(idChunk); if (thePreFetchIndexes) { - entityIds = loadedResourceTableEntries.stream() - .filter(ResourceTable::isParamsStringPopulated) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - preFetchIndexes(entityIds, "string", "myParamsString", null); - } - entityIds = loadedResourceTableEntries.stream() - .filter(ResourceTable::isParamsTokenPopulated) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - preFetchIndexes(entityIds, "token", "myParamsToken", null); - } + prefetchByField("string", "myParamsString", ResourceTable::isParamsStringPopulated, entityChunk); + prefetchByField("token", "myParamsToken", ResourceTable::isParamsTokenPopulated, entityChunk); + prefetchByField("date", "myParamsDate", ResourceTable::isParamsDatePopulated, entityChunk); + prefetchByField( + "quantity", "myParamsQuantity", ResourceTable::isParamsQuantityPopulated, entityChunk); + prefetchByField("resourceLinks", "myResourceLinks", ResourceTable::isHasLinks, entityChunk); - entityIds = loadedResourceTableEntries.stream() - .filter(ResourceTable::isParamsDatePopulated) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - preFetchIndexes(entityIds, "date", "myParamsDate", null); - } + prefetchByJoinClause( + "tags", + // fetch the TagResources and the actual TagDefinitions + "LEFT JOIN FETCH r.myTags t LEFT JOIN FETCH t.myTag", + BaseHasResource::isHasTags, + entityChunk); - entityIds = loadedResourceTableEntries.stream() - .filter(ResourceTable::isParamsQuantityPopulated) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - preFetchIndexes(entityIds, "quantity", "myParamsQuantity", null); - } - - entityIds = loadedResourceTableEntries.stream() - .filter(ResourceTable::isHasLinks) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - preFetchIndexes(entityIds, "resourceLinks", "myResourceLinks", null); - } - - entityIds = loadedResourceTableEntries.stream() - .filter(BaseHasResource::isHasTags) - .map(ResourceTable::getId) - .collect(Collectors.toList()); - if (entityIds.size() > 0) { - myResourceTagDao.findByResourceIds(entityIds); - preFetchIndexes(entityIds, "tags", "myTags", null); - } - - entityIds = loadedResourceTableEntries.stream() - .map(ResourceTable::getId) - .collect(Collectors.toList()); if (myStorageSettings.getIndexMissingFields() == JpaStorageSettings.IndexEnabledEnum.ENABLED) { - preFetchIndexes(entityIds, "searchParamPresence", "mySearchParamPresents", null); + prefetchByField("searchParamPresence", "mySearchParamPresents", r -> true, entityChunk); } } - - new QueryChunker() - .chunk(loadedResourceTableEntries, SearchBuilder.getMaximumPageSize() / 2, entries -> { - Map entities = - entries.stream().collect(Collectors.toMap(ResourceTable::getId, t -> t)); - - CriteriaBuilder b = myEntityManager.getCriteriaBuilder(); - CriteriaQuery q = b.createQuery(ResourceHistoryTable.class); - Root from = q.from(ResourceHistoryTable.class); - - from.fetch("myProvenance", JoinType.LEFT); - - List orPredicates = new ArrayList<>(); - for (ResourceTable next : entries) { - Predicate resId = b.equal(from.get("myResourceId"), next.getId()); - Predicate resVer = b.equal(from.get("myResourceVersion"), next.getVersion()); - orPredicates.add(b.and(resId, resVer)); - } - q.where(b.or(orPredicates.toArray(EMPTY_PREDICATE_ARRAY))); - List resultList = - myEntityManager.createQuery(q).getResultList(); - for (ResourceHistoryTable next : resultList) { - ResourceTable nextEntity = entities.get(next.getResourceId()); - if (nextEntity != null) { - nextEntity.setCurrentVersionEntity(next); - } - } - }); } }); } - private void preFetchIndexes( - List theIds, - String typeDesc, - String fieldName, - @Nullable List theEntityListToPopulate) { - new QueryChunker().chunk(theIds, ids -> { - TypedQuery query = myEntityManager.createQuery( - "FROM ResourceTable r LEFT JOIN FETCH r." + fieldName + " WHERE r.myId IN ( :IDS )", - ResourceTable.class); - query.setParameter("IDS", ids); - List indexFetchOutcome = query.getResultList(); - ourLog.debug("Pre-fetched {} {}} indexes", indexFetchOutcome.size(), typeDesc); - if (theEntityListToPopulate != null) { - theEntityListToPopulate.addAll(indexFetchOutcome); - } - }); + @Nonnull + private List prefetchResourceTableHistoryAndProvenance(List idChunk) { + assert idChunk.size() < SearchConstants.MAX_PAGE_SIZE : "assume pre-chunked"; + + Query query = myEntityManager.createQuery("select r, h " + + " FROM ResourceTable r " + + " LEFT JOIN fetch ResourceHistoryTable h " + + " on r.myVersion = h.myResourceVersion and r.id = h.myResourceId " + + " left join fetch h.myProvenance " + + " WHERE r.myId IN ( :IDS ) "); + query.setParameter("IDS", idChunk); + + @SuppressWarnings("unchecked") + Stream queryResultStream = query.getResultStream(); + return queryResultStream + .map(nextPair -> { + // Store the matching ResourceHistoryTable in the transient slot on ResourceTable + ResourceTable result = (ResourceTable) nextPair[0]; + ResourceHistoryTable currentVersion = (ResourceHistoryTable) nextPair[1]; + result.setCurrentVersionEntity(currentVersion); + return result; + }) + .collect(Collectors.toList()); + } + + /** + * Prefetch a join field for the active subset of some ResourceTable entities. + * Convenience wrapper around prefetchByJoinClause() for simple fields. + * + * @param theDescription for logging + * @param theJpaFieldName the join field from ResourceTable + * @param theEntityPredicate select which ResourceTable entities need this join + * @param theEntities the ResourceTable entities to consider + */ + private void prefetchByField( + String theDescription, + String theJpaFieldName, + Predicate theEntityPredicate, + List theEntities) { + + String joinClause = "LEFT JOIN FETCH r." + theJpaFieldName; + + prefetchByJoinClause(theDescription, joinClause, theEntityPredicate, theEntities); + } + + /** + * Prefetch a join field for the active subset of some ResourceTable entities. + * + * @param theDescription for logging + * @param theJoinClause the JPA join expression to add to `ResourceTable r` + * @param theEntityPredicate selects which entities need this prefetch + * @param theEntities the ResourceTable entities to consider + */ + private void prefetchByJoinClause( + String theDescription, + String theJoinClause, + Predicate theEntityPredicate, + List theEntities) { + + // Which entities need this prefetch? + List idSubset = theEntities.stream() + .filter(theEntityPredicate) + .map(ResourceTable::getId) + .collect(Collectors.toList()); + + if (idSubset.isEmpty()) { + // nothing to do + return; + } + + String jqlQuery = "FROM ResourceTable r " + theJoinClause + " WHERE r.myId IN ( :IDS )"; + + TypedQuery query = myEntityManager.createQuery(jqlQuery, ResourceTable.class); + query.setParameter("IDS", idSubset); + List indexFetchOutcome = query.getResultList(); + + ourLog.debug("Pre-fetched {} {} indexes", indexFetchOutcome.size(), theDescription); } @Nullable diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java deleted file mode 100644 index 601a4af0101..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2024 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.dao.data; - -import ca.uhn.fhir.jpa.model.entity.ForcedId; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -/** - * Legacy forced_id implementation. - * - * @deprecated we now have a fhir_id column directly on HFJ_RESOURCE. - * No runtime code should query this table except for deletions by PK. - * To be deleted in 2024 (zero-downtime). - */ -@Deprecated(since = "6.10") -@Repository -public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository { - - @Modifying - @Query("DELETE FROM ForcedId t WHERE t.myId = :pid") - void deleteByPid(@Param("pid") Long theId); -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceLinkDao.java index 641db53d0bf..f848f23eb1a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceLinkDao.java @@ -43,7 +43,6 @@ public interface IResourceLinkDao extends JpaRepository, IHa * Loads a collection of ResourceLink entities by PID, but also eagerly fetches * the target resources and their forced IDs */ - @Query( - "SELECT t FROM ResourceLink t LEFT JOIN FETCH t.myTargetResource tr LEFT JOIN FETCH tr.myForcedId WHERE t.myId in :pids") + @Query("SELECT t FROM ResourceLink t LEFT JOIN FETCH t.myTargetResource tr WHERE t.myId in :pids") List findByPidAndFetchTargetDetails(@Param("pids") List thePids); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java index a054d4b06ec..21f233891e5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java @@ -177,25 +177,26 @@ public interface IResourceTableDao @Query("SELECT t.myId, t.myResourceType, t.myVersion FROM ResourceTable t WHERE t.myId IN ( :pid )") Collection getResourceVersionsForPid(@Param("pid") List pid); - @Query( - "SELECT t FROM ResourceTable t LEFT JOIN FETCH t.myForcedId WHERE t.myPartitionId.myPartitionId IS NULL AND t.myId = :pid") + @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId IS NULL AND t.myId = :pid") Optional readByPartitionIdNull(@Param("pid") Long theResourceId); - @Query( - "SELECT t FROM ResourceTable t LEFT JOIN FETCH t.myForcedId WHERE t.myPartitionId.myPartitionId = :partitionId AND t.myId = :pid") + @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId = :partitionId AND t.myId = :pid") Optional readByPartitionId( @Param("partitionId") int thePartitionId, @Param("pid") Long theResourceId); @Query( - "SELECT t FROM ResourceTable t LEFT JOIN FETCH t.myForcedId WHERE (t.myPartitionId.myPartitionId IS NULL OR t.myPartitionId.myPartitionId IN (:partitionIds)) AND t.myId = :pid") + "SELECT t FROM ResourceTable t WHERE (t.myPartitionId.myPartitionId IS NULL OR t.myPartitionId.myPartitionId IN (:partitionIds)) AND t.myId = :pid") Optional readByPartitionIdsOrNull( @Param("partitionIds") Collection thrValues, @Param("pid") Long theResourceId); - @Query( - "SELECT t FROM ResourceTable t LEFT JOIN FETCH t.myForcedId WHERE t.myPartitionId.myPartitionId IN (:partitionIds) AND t.myId = :pid") + @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId IN (:partitionIds) AND t.myId = :pid") Optional readByPartitionIds( @Param("partitionIds") Collection thrValues, @Param("pid") Long theResourceId); - @Query("SELECT t FROM ResourceTable t LEFT JOIN FETCH t.myForcedId WHERE t.myId IN :pids") + @Query("SELECT t FROM ResourceTable t WHERE t.myId IN :pids") List findAllByIdAndLoadForcedIds(@Param("pids") List thePids); + + @Query("SELECT t FROM ResourceTable t where t.myResourceType = :restype and t.myFhirId = :fhirId") + Optional findByTypeAndFhirId( + @Param("restype") String theResourceName, @Param("fhirId") String theFhirId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index d31c7134f4b..cc5fb0795e5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -53,9 +53,7 @@ public interface ITermValueSetDao extends JpaRepository, IHa * The current TermValueSet is not necessarily the last uploaded anymore, but the current VS resource * is pointed by a specific ForcedId, so we locate current ValueSet as the one pointing to current VS resource */ - @Query( - value = - "SELECT vs FROM ForcedId f, TermValueSet vs where f.myForcedId = :forcedId and vs.myResource = f.myResource") + @Query(value = "SELECT vs FROM TermValueSet vs where vs.myResource.myFhirId = :forcedId ") Optional findTermValueSetByForcedId(@Param("forcedId") String theForcedId); @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url AND vs.myVersion IS NULL") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index 6c254ac71a8..b6427e80f75 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -47,7 +47,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptProperty; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity; @@ -174,7 +173,6 @@ public class ExpungeEverythingService implements IExpungeEverythingService { expungeEverythingByTypeWithoutPurging(theRequest, BulkImportJobFileEntity.class, requestPartitionId)); counter.addAndGet( expungeEverythingByTypeWithoutPurging(theRequest, BulkImportJobEntity.class, requestPartitionId)); - counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ForcedId.class, requestPartitionId)); counter.addAndGet(expungeEverythingByTypeWithoutPurging( theRequest, ResourceIndexedSearchParamDate.class, requestPartitionId)); counter.addAndGet(expungeEverythingByTypeWithoutPurging( diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/JpaResourceExpungeService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/JpaResourceExpungeService.java index 450f412690b..4e8e3ea7e83 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/JpaResourceExpungeService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/JpaResourceExpungeService.java @@ -27,7 +27,6 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao; @@ -46,7 +45,6 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceTagDao; import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.util.MemoryCacheService; @@ -79,9 +77,6 @@ import java.util.concurrent.atomic.AtomicInteger; public class JpaResourceExpungeService implements IResourceExpungeService { private static final Logger ourLog = LoggerFactory.getLogger(JpaResourceExpungeService.class); - @Autowired - private IForcedIdDao myForcedIdDao; - @Autowired private IResourceTableDao myResourceTableDao; @@ -323,11 +318,6 @@ public class JpaResourceExpungeService implements IResourceExpungeService { init680(); init680_Part2(); init700(); + init720(); + } + + protected void init720() { + // Start of migrations from 7.0 to 7.2 + + Builder version = forVersion(VersionEnum.V7_2_0); + + // allow null codes in concept map targets + version.onTable("TRM_CONCEPT_MAP_GRP_ELM_TGT") + .modifyColumn("20240327.1", "TARGET_CODE") + .nullable() + .withType(ColumnTypeEnum.STRING, 500); + + // Stop writing to hfj_forced_id https://github.com/hapifhir/hapi-fhir/pull/5817 + Builder.BuilderWithTableName forcedId = version.onTable("HFJ_FORCED_ID"); + forcedId.dropForeignKey("20240402.1", "FK_FORCEDID_RESOURCE", "HFJ_RESOURCE"); + forcedId.dropIndex("20240402.2", "IDX_FORCEDID_RESID"); + forcedId.dropIndex("20240402.3", "IDX_FORCEDID_TYPE_FID"); + forcedId.dropIndex("20240402.4", "IDX_FORCEID_FID"); } protected void init700() { @@ -1398,9 +1418,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .addIndex("20211210.4", "FK_FORCEDID_RESOURCE") .unique(true) .withColumns("RESOURCE_PID") - .doNothing() // This migration was added in error, as this table already has a unique constraint on // RESOURCE_PID and every database creates an index on anything that is unique. - .onlyAppliesToPlatforms(NON_AUTOMATIC_FK_INDEX_PLATFORMS); + .onlyAppliesToPlatforms(NON_AUTOMATIC_FK_INDEX_PLATFORMS) + .doNothing(); // This migration was added in error, as this table already has a unique constraint on } private void init570() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java index 7cfdfe5245a..d17ced8b4cb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java @@ -51,6 +51,7 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -416,6 +417,10 @@ public class TermConceptClientMappingSvcImpl implements ITermConceptClientMappin theTranslationResult.setResult(false); msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound"); theTranslationResult.setMessage(msg); + } else if (isOnlyNegativeMatches(theTranslationResult)) { + theTranslationResult.setResult(false); + msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "onlyNegativeMatchesFound"); + theTranslationResult.setMessage(msg); } else { theTranslationResult.setResult(true); msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound"); @@ -423,6 +428,21 @@ public class TermConceptClientMappingSvcImpl implements ITermConceptClientMappin } } + /** + * Evaluates whether a translation result contains any positive matches or only negative ones. This is required + * because the FHIR specification states + * that the result field "can only be true if at least one returned match has an equivalence which is not unmatched + * or disjoint". + * @param theTranslationResult the translation result to be evaluated + * @return true if all the potential matches in the result have a negative valence (i.e., "unmatched" and "disjoint") + */ + private boolean isOnlyNegativeMatches(TranslateConceptResults theTranslationResult) { + return theTranslationResult.getResults().stream() + .map(TranslateConceptResult::getEquivalence) + .allMatch(t -> StringUtils.equals(Enumerations.ConceptMapEquivalence.UNMATCHED.toCode(), t) + || StringUtils.equals(Enumerations.ConceptMapEquivalence.DISJOINT.toCode(), t)); + } + private boolean alreadyContainsMapping( List elements, TranslateConceptResult translationMatch) { for (TranslateConceptResult nextExistingElement : elements) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java index 73f2069e178..bb4e00383dc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java @@ -41,6 +41,7 @@ import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; @@ -218,13 +219,17 @@ public class TermConceptMappingSvcImpl extends TermConceptClientMappingSvcImpl i if (element.hasTarget()) { TermConceptMapGroupElementTarget termConceptMapGroupElementTarget; for (ConceptMap.TargetElementComponent elementTarget : element.getTarget()) { - if (isBlank(elementTarget.getCode())) { + if (isBlank(elementTarget.getCode()) + && elementTarget.getEquivalence() + != Enumerations.ConceptMapEquivalence.UNMATCHED) { continue; } termConceptMapGroupElementTarget = new TermConceptMapGroupElementTarget(); termConceptMapGroupElementTarget.setConceptMapGroupElement(termConceptMapGroupElement); - termConceptMapGroupElementTarget.setCode(elementTarget.getCode()); - termConceptMapGroupElementTarget.setDisplay(elementTarget.getDisplay()); + if (isNotBlank(elementTarget.getCode())) { + termConceptMapGroupElementTarget.setCode(elementTarget.getCode()); + termConceptMapGroupElementTarget.setDisplay(elementTarget.getDisplay()); + } termConceptMapGroupElementTarget.setEquivalence(elementTarget.getEquivalence()); termConceptMapGroupElement .getConceptMapGroupElementTargets() diff --git a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql index bd7125b4b87..2677ad6a743 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql +++ b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql @@ -1,5 +1,5 @@ --- we can't use convering index until the autovacuum runs for those rows, which kills index performance +-- we can't use covering index until the autovacuum runs for those rows, which kills index performance ALTER TABLE hfj_resource SET (autovacuum_vacuum_scale_factor = 0.01); ALTER TABLE hfj_forced_id SET (autovacuum_vacuum_scale_factor = 0.01); ALTER TABLE hfj_res_link SET (autovacuum_vacuum_scale_factor = 0.01); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index e969a0be649..17c3a08aa50 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 6de7d563dd7..99bed5fc6d0 100644 --- a/hapi-fhir-jpaserver-hfql/pom.xml +++ b/hapi-fhir-jpaserver-hfql/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index dbb537202f7..eb66d27cfac 100644 --- a/hapi-fhir-jpaserver-ips/pom.xml +++ b/hapi-fhir-jpaserver-ips/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 6441a09562d..2a8ed1e5e9b 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index b44506f53ab..23b8a5f67c5 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java index 5da18587c48..aee59d74ff4 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java @@ -29,7 +29,6 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; -import jakarta.persistence.Transient; import org.hibernate.annotations.OptimisticLock; import java.util.Collection; @@ -65,22 +64,6 @@ public abstract class BaseHasResource extends BasePartitionable @OptimisticLock(excluded = true) private Date myUpdated; - /** - * This is stored as an optimization to avoid needing to query for this - * after an update - */ - @Transient - // TODO MB forced_id delete this in step 3 - private transient String myTransientForcedId; - - public String getTransientForcedId() { - return myTransientForcedId; - } - - public void setTransientForcedId(String theTransientForcedId) { - myTransientForcedId = theTransientForcedId; - } - public abstract BaseTag addTag(TagDefinition theDef); @Override @@ -97,13 +80,6 @@ public abstract class BaseHasResource extends BasePartitionable myFhirVersion = theFhirVersion; } - public abstract ForcedId getForcedId(); - - public abstract void setForcedId(ForcedId theForcedId); - - @Override - public abstract Long getId(); - public void setDeleted(Date theDate) { myDeleted = theDate; } @@ -129,12 +105,6 @@ public abstract class BaseHasResource extends BasePartitionable myPublished = thePublished.getValue(); } - @Override - public abstract Long getResourceId(); - - @Override - public abstract String getResourceType(); - public abstract Collection getTags(); @Override @@ -151,9 +121,6 @@ public abstract class BaseHasResource extends BasePartitionable myUpdated = theUpdated; } - @Override - public abstract long getVersion(); - @Override public boolean isHasTags() { return myHasTags; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java index 9c69541511a..cab74929dfd 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java @@ -21,51 +21,24 @@ package ca.uhn.fhir.jpa.model.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.ForeignKey; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.annotations.ColumnDefault; +/** + * The old way we handled client-assigned resource ids. + * Replaced by {@link ResourceTable#myFhirId}. + * @deprecated This is unused, and only kept for history and upgrade migration testing. + */ @Entity() -@Table( - name = ForcedId.HFJ_FORCED_ID, - uniqueConstraints = { - @UniqueConstraint( - name = "IDX_FORCEDID_RESID", - columnNames = {"RESOURCE_PID"}), - - /* - * This index is called IDX_FORCEDID_TYPE_FID and guarantees - * uniqueness of RESOURCE_TYPE,FORCED_ID. This doesn't make sense - * for partitioned servers, so we replace it on those servers - * with IDX_FORCEDID_TYPE_PFID covering - * PARTITION_ID,RESOURCE_TYPE,FORCED_ID - */ - @UniqueConstraint( - name = ForcedId.IDX_FORCEDID_TYPE_FID, - columnNames = {"RESOURCE_TYPE", "FORCED_ID"}) - }, - indexes = { - /* - * NB: We previously had indexes named - * - IDX_FORCEDID_TYPE_FORCEDID - * - IDX_FORCEDID_TYPE_RESID - * so don't reuse these names - */ - @Index(name = "IDX_FORCEID_FID", columnList = "FORCED_ID"), - // @Index(name = "IDX_FORCEID_RESID", columnList = "RESOURCE_PID"), - }) -public class ForcedId extends BasePartitionable { +@Table(name = ForcedId.HFJ_FORCED_ID) +@Deprecated(since = "7.1", forRemoval = true) +class ForcedId extends BasePartitionable { public static final int MAX_FORCED_ID_LENGTH = 100; public static final String IDX_FORCEDID_TYPE_FID = "IDX_FORCEDID_TYPE_FID"; @@ -80,14 +53,6 @@ public class ForcedId extends BasePartitionable { @Column(name = "PID") private Long myId; - @JoinColumn( - name = "RESOURCE_PID", - nullable = false, - updatable = false, - foreignKey = @ForeignKey(name = "FK_FORCEDID_RESOURCE")) - @OneToOne(fetch = FetchType.LAZY) - private ResourceTable myResource; - @Column(name = "RESOURCE_PID", nullable = false, updatable = false, insertable = false) private Long myResourcePid; @@ -112,10 +77,6 @@ public class ForcedId extends BasePartitionable { myForcedId = theForcedId; } - public void setResource(ResourceTable theResource) { - myResource = theResource; - } - public String getResourceType() { return myResourceType; } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java index 49ad436112c..6348ce579b3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java @@ -22,7 +22,26 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.Length; @@ -111,6 +130,12 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl @Transient private transient ResourceHistoryProvenanceEntity myNewHistoryProvenanceEntity; + /** + * This is stored as an optimization to avoid needing to fetch ResourceTable + * to access the resource id. + */ + @Transient + private transient String myTransientForcedId; /** * Constructor @@ -280,16 +305,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl return new IdDt(getResourceType() + '/' + resourceIdPart + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } - @Override - public ForcedId getForcedId() { - return getResourceTable().getForcedId(); - } - - @Override - public void setForcedId(ForcedId theForcedId) { - getResourceTable().setForcedId(theForcedId); - } - /** * Returns true if there is a populated resource text (i.e. * either {@link #getResource()} or {@link #getResourceTextVc()} return a non null @@ -311,4 +326,12 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl } return myNewHistoryProvenanceEntity; } + + public String getTransientForcedId() { + return myTransientForcedId; + } + + public void setTransientForcedId(String theTransientForcedId) { + myTransientForcedId = theTransientForcedId; + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index 4aad4fa503a..39f85198aec 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -38,7 +38,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.NamedEntityGraph; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.PostPersist; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; @@ -420,15 +419,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @Transient private transient boolean myVersionUpdatedInCurrentTransaction; - @OneToOne( - optional = true, - fetch = FetchType.EAGER, - cascade = {}, - orphanRemoval = false, - mappedBy = "myResource") - @OptimisticLock(excluded = true) - private ForcedId myForcedId; - @Transient private volatile String myCreatedByMatchUrl; @@ -889,10 +879,9 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas retVal.setResourceId(myId); retVal.setResourceType(myResourceType); - retVal.setTransientForcedId(getTransientForcedId()); + retVal.setTransientForcedId(getFhirId()); retVal.setFhirVersion(getFhirVersion()); retVal.setResourceTable(this); - retVal.setForcedId(getForcedId()); retVal.setPartitionId(getPartitionId()); retVal.setHasTags(isHasTags()); @@ -923,6 +912,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas public String toString() { ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); b.append("pid", myId); + b.append("fhirId", myFhirId); b.append("resourceType", myResourceType); b.append("version", myVersion); if (getPartitionId() != null) { @@ -970,16 +960,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas return JpaPid.fromId(getId()); } - @Override - public ForcedId getForcedId() { - return myForcedId; - } - - @Override - public void setForcedId(ForcedId theForcedId) { - myForcedId = theForcedId; - } - @Override public IdDt getIdDt() { IdDt retVal = new IdDt(); @@ -997,10 +977,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas String resourceId; if (myFhirId != null && !myFhirId.isEmpty()) { resourceId = myFhirId; - } else if (getTransientForcedId() != null) { - resourceId = getTransientForcedId(); - } else if (myForcedId != null) { - resourceId = myForcedId.getForcedId(); } else { Long id = this.getResourceId(); resourceId = Long.toString(id); diff --git a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java index a5ccb698720..e3534ba81d6 100644 --- a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java +++ b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java @@ -25,17 +25,15 @@ public class ResourceTableTest { @ParameterizedTest @CsvSource(value={ + "123, null, Patient/123/_history/1", "123, 123, Patient/123/_history/1", - ", 123, Patient/123/_history/1", - "null, 456, Patient/456/_history/1" + "123, 456, Patient/456/_history/1" },nullValues={"null"}) - public void testPopulateId(String theFhirId, String theForcedId, String theExpected) { + public void testPopulateId(Long theResId, String theFhirId, String theExpected) { // Given ResourceTable t = new ResourceTable(); + t.setId(theResId); t.setFhirId(theFhirId); - ForcedId forcedId = new ForcedId(); - forcedId.setForcedId(theForcedId); - t.setForcedId(forcedId); t.setResourceType(new Patient().getResourceType().name()); t.setVersionForUnitTest(1); diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 751edb8b63f..1cbb7748009 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 4e05e2edf03..cc08fd09695 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index d51a8940bc0..1ced5f66cf1 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index aa2c0803c9e..c27ecc6bc2d 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseStorageDao; import ca.uhn.fhir.jpa.dao.DaoTestUtils; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; @@ -82,7 +81,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.util.ArrayList; @@ -112,8 +110,6 @@ import static org.junit.jupiter.api.Assertions.fail; public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2Test.class); - @Autowired - private IForcedIdDao myForcedIdDao; @AfterEach public final void after() { @@ -995,9 +991,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { idv2 = myPatientDao.update(patient, mySrd).getId(); } - runInTransaction(() -> { - ourLog.info("Forced IDs:\n{}", myForcedIdDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n"))); - }); + logAllResources(); List patients = toList(myPatientDao.history(idv1.toVersionless(), null, null, null, mySrd)); assertTrue(patients.size() == 2); diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderLanguageParamDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderLanguageParamDstu2Test.java new file mode 100644 index 00000000000..a69593a0490 --- /dev/null +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderLanguageParamDstu2Test.java @@ -0,0 +1,67 @@ +package ca.uhn.fhir.jpa.provider; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.model.primitive.CodeDt; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.resource.Bundle; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ResourceProviderLanguageParamDstu2Test extends BaseResourceProviderDstu2Test { + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamEnabled() { + myStorageSettings.setLanguageSearchParameterEnabled(true); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage(new CodeDt("en")); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage(new CodeDt("fr")); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + List foundResources; + Bundle result; + + result = myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + + foundResources = toUnqualifiedVersionlessIdValues(result); + assertThat(foundResources, contains(patId.getValue())); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamDisabled() { + myStorageSettings.setLanguageSearchParameterEnabled(new JpaStorageSettings().isLanguageSearchParameterEnabled()); + mySearchParamRegistry.forceRefresh(); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + }); + assertThat(exception.getMessage(), containsString(Msg.code(1223))); + } +} diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 7fa9c4fbb06..7868b2b95a2 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 7b2b699d85f..336d1ac0478 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -626,16 +626,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { .getSingleResult(); assertNotNull(readBackView, "found search view"); - // verify the forced id join still works - if (readBackResource.getForcedId() != null) { - assertEquals(myExpectedId, readBackResource.getForcedId().getForcedId(), - "legacy join populated"); - assertEquals(myExpectedId, readBackView.getFhirId(), - "legacy join populated"); - } else { - assertEquals(IdStrategyEnum.SEQUENTIAL_NUMERIC, theServerIdStrategy, - "hfj_forced_id join column is only empty when using server-assigned ids"); - } } + assertEquals(myExpectedId, readBackView.getFhirId(), + "fhir_id populated"); + } } @Test diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderLanguageParamDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderLanguageParamDstu3Test.java new file mode 100644 index 00000000000..523467c83d4 --- /dev/null +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderLanguageParamDstu3Test.java @@ -0,0 +1,84 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Patient; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ResourceProviderLanguageParamDstu3Test extends BaseResourceProviderDstu3Test { + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamEnabled() { + myStorageSettings.setLanguageSearchParameterEnabled(true); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage("en"); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage("fr"); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap map; + IBundleProvider results; + List foundResources; + Bundle result; + + result = myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + + foundResources = toUnqualifiedVersionlessIdValues(result); + assertThat(foundResources, contains(patId.getValue())); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamDisabled() { + myStorageSettings.setLanguageSearchParameterEnabled(new JpaStorageSettings().isLanguageSearchParameterEnabled()); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage("en"); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage("fr"); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap map; + IBundleProvider results; + List foundResources; + Bundle result; + + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + }); + assertThat(exception.getMessage(), containsString(Msg.code(1223))); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 8bec81531a4..9a9709161a5 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java index 6b600251e3a..cc51d896f46 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java @@ -321,6 +321,61 @@ public class Batch2CoordinatorIT extends BaseJpaR4Test { } } + @Test + public void reductionStepFailing_willFailJob() throws InterruptedException { + // setup + String jobId = new Exception().getStackTrace()[0].getMethodName(); + int totalChunks = 3; + AtomicInteger chunkCounter = new AtomicInteger(); + String error = "this is an error"; + + buildAndDefine3StepReductionJob(jobId, new IReductionStepHandler() { + + @Override + public void firstStep(StepExecutionDetails theStep, IJobDataSink theDataSink) { + for (int i = 0; i < totalChunks; i++) { + theDataSink.accept(new FirstStepOutput()); + } + } + + @Override + public void secondStep(StepExecutionDetails theStep, IJobDataSink theDataSink) { + SecondStepOutput output = new SecondStepOutput(); + theDataSink.accept(output); + } + + @Override + public void reductionStepConsume(ChunkExecutionDetails theChunkDetails, IJobDataSink theDataSink) { + chunkCounter.getAndIncrement(); + } + + @Override + public void reductionStepRun(StepExecutionDetails theStepExecutionDetails, IJobDataSink theDataSink) { + // always throw + throw new RuntimeException(error); + } + }); + + // test + JobInstanceStartRequest request = buildRequest(jobId); + myFirstStepLatch.setExpectedCount(1); + Batch2JobStartResponse startResponse = myJobCoordinator.startInstance(new SystemRequestDetails(), request); + String instanceId = startResponse.getInstanceId(); + assertNotNull(instanceId); + + // waiting for job to end (any status - but we'll verify failed later) + myBatch2JobHelper.awaitJobHasStatus(instanceId, StatusEnum.getEndedStatuses().toArray(new StatusEnum[0])); + + // verify + Optional instanceOp = myJobPersistence.fetchInstance(instanceId); + assertTrue(instanceOp.isPresent()); + JobInstance jobInstance = instanceOp.get(); + + assertEquals(totalChunks, chunkCounter.get()); + + assertEquals(StatusEnum.FAILED, jobInstance.getStatus()); + } + @Test public void testJobWithReductionStepFiresCompletionHandler() throws InterruptedException { // setup diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java index 5462f896a3c..2076cf970fa 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java @@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.bulk.imprt2; import ca.uhn.fhir.batch2.api.JobExecutionFailedException; import ca.uhn.fhir.batch2.jobs.imprt.ConsumeFilesStep; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.dao.r4.BasePartitioningR4Test; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; @@ -84,7 +83,7 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { // Validate - assertEquals(7, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread(), myCaptureQueriesListener.getInsertQueriesForCurrentThread().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n"))); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -145,9 +144,9 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { // Validate if (partitionEnabled) { - assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - } else { assertEquals(7, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + } else { + assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); } assertEquals(2, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(4, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); @@ -190,7 +189,7 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), either(containsString("rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='B' and rt1_0.PARTITION_ID is null or rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='A' and rt1_0.PARTITION_ID is null")) .or(containsString("rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='A' and rt1_0.PARTITION_ID is null or rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='B' and rt1_0.PARTITION_ID is null"))); - assertEquals(52, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(50, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); assertEquals(1, myCaptureQueriesListener.countCommits()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java index c68d4a77b1d..7eb90f46452 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java @@ -19,7 +19,6 @@ import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.delete.DeleteConflictService; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.search.ResourceSearchUrlSvc; @@ -227,7 +226,8 @@ class BaseHapiFhirResourceDaoTest { RequestPartitionId partitionId = Mockito.mock(RequestPartitionId.class); JpaPid jpaPid = JpaPid.fromIdAndVersion(123L, 1L); ResourceTable entity = new ResourceTable(); - entity.setForcedId(new ForcedId()); + entity.setId(123L); + entity.setFhirId("456"); // mock when(myRequestPartitionHelperSvc.determineReadPartitionForRequestForRead( diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index e91e3421bf2..374b7e3d565 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -7,7 +7,6 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; @@ -30,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_TYPE_FHIR_ID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -61,13 +61,6 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { mySrdInterceptorService.unregisterInterceptorsIf(t -> t instanceof MyReadWriteInterceptor); - if (myHaveDroppedForcedIdUniqueConstraint) { - runInTransaction(() -> { - myEntityManager.createNativeQuery("delete from HFJ_FORCED_ID").executeUpdate(); - myEntityManager.createNativeQuery("alter table HFJ_FORCED_ID add constraint IDX_FORCEDID_TYPE_FID unique (RESOURCE_TYPE, FORCED_ID)"); - }); - } - myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields()); myStorageSettings.setAutoCreatePlaceholderReferenceTargets(new JpaStorageSettings().isAutoCreatePlaceholderReferenceTargets()); myStorageSettings.setMassIngestionMode(new JpaStorageSettings().isMassIngestionMode()); @@ -106,6 +99,18 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { } + @Override + public void afterPurgeDatabase() { + super.afterPurgeDatabase(); + + if (myHaveDroppedForcedIdUniqueConstraint) { + runInTransaction(() -> { + myEntityManager.createNativeQuery("delete from HFJ_RESOURCE").executeUpdate(); + myEntityManager.createNativeQuery("alter table " + ResourceTable.HFJ_RESOURCE + + " add constraint " + IDX_RES_TYPE_FHIR_ID + " unique (RES_TYPE, FHIR_ID)").executeUpdate(); + }); + } + } protected void createUniqueCompositeSp() { addCreateDefaultPartition(); addReadDefaultPartition(); // one for search param validation @@ -137,8 +142,7 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { protected void dropForcedIdUniqueConstraint() { runInTransaction(() -> { - myEntityManager.createNativeQuery("alter table " + ForcedId.HFJ_FORCED_ID + " drop constraint " + ForcedId.IDX_FORCEDID_TYPE_FID).executeUpdate(); - myEntityManager.createNativeQuery("alter table " + ResourceTable.HFJ_RESOURCE + " drop constraint " + ResourceTable.IDX_RES_TYPE_FHIR_ID).executeUpdate(); + myEntityManager.createNativeQuery("alter table " + ResourceTable.HFJ_RESOURCE + " drop constraint " + IDX_RES_TYPE_FHIR_ID).executeUpdate(); }); myHaveDroppedForcedIdUniqueConstraint = true; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index 9e24d3e728a..abba1fbe50c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -20,7 +20,6 @@ import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.interceptor.ForceOffsetSearchModeInterceptor; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; @@ -47,6 +46,7 @@ import ca.uhn.fhir.test.utilities.ProxyUtil; import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleBuilder; +import jakarta.annotation.Nonnull; import org.hamcrest.CoreMatchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -90,7 +90,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.util.comparator.ComparableComparator; -import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -150,7 +149,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test @Autowired private ISubscriptionTriggeringSvc mySubscriptionTriggeringSvc; @Autowired - private ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;; + private ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc; @Autowired private ReindexStep myReindexStep; @Autowired @@ -221,7 +220,6 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test runInTransaction(() -> assertThat(myResourceTableDao.findAll(), not(empty()))); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), not(empty()))); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), not(empty()))); logAllResources(); @@ -242,11 +240,10 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(47, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); - assertEquals(85, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + assertEquals(80, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); } @@ -328,7 +325,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); Group group = createGroup(patientList.subList(0, initialPatientsCount)); - assertQueryCount(31, 0, 4, 0); + assertQueryCount(31, 0, 3, 0); myCaptureQueriesListener.clear(); group = updateGroup(group, patientList.subList(initialPatientsCount, allPatientsCount)); @@ -350,7 +347,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); Group group = createGroup(patientList); - assertQueryCount(31, 0, 4, 0); + assertQueryCount(31, 0, 3, 0); // Make a change to the group, but don't touch any references in it myCaptureQueriesListener.clear(); @@ -672,7 +669,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); - assertEquals(4, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); + assertEquals(3, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); @@ -705,24 +702,20 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); - assertEquals(4, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); + assertEquals(3, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); runInTransaction(() -> { - List allForcedIds = myForcedIdDao.findAll(); - for (ForcedId next : allForcedIds) { - assertNotNull(next.getResourceId()); - assertNotNull(next.getForcedId()); - } - List resources = myResourceTableDao.findAll(); String versions = "Resource Versions:\n * " + resources.stream().map(t -> "Resource " + t.getIdDt() + " has version: " + t.getVersion()).collect(Collectors.joining("\n * ")); for (ResourceTable next : resources) { assertEquals(1, next.getVersion(), versions); + assertNotNull(next.getResourceId()); + assertNotNull(next.getFhirId()); } }); @@ -771,22 +764,18 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); - assertEquals(4, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); + assertEquals(3, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); runInTransaction(() -> { - List allForcedIds = myForcedIdDao.findAll(); - for (ForcedId next : allForcedIds) { - assertNotNull(next.getResourceId()); - assertNotNull(next.getForcedId()); - } - List resources = myResourceTableDao.findAll(); String versions = "Resource Versions:\n * " + resources.stream().map(t -> "Resource " + t.getIdDt() + " has version: " + t.getVersion()).collect(Collectors.joining("\n * ")); for (ResourceTable next : resources) { assertEquals(1, next.getVersion(), versions); + assertNotNull(next.getResourceId()); + assertNotNull(next.getFhirId()); } }); @@ -819,7 +808,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); - assertEquals(4, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); + assertEquals(3, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); } @@ -838,7 +827,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test DeleteMethodOutcome outcome = myPatientDao.deleteByUrl("Patient?active=true", new SystemRequestDetails()); // Validate - assertEquals(13, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(12, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(10, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(10, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(30, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -856,7 +845,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test withFamily("Family" + i), withTag("http://foo", "blah")); } - List pids = runInTransaction(() -> myForcedIdDao + List pids = runInTransaction(() -> myResourceTableDao .findAll() .stream() .map(t -> new TypedPidJson(t.getResourceType(), Long.toString(t.getResourceId()))) @@ -874,7 +863,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); - assertEquals(29, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + assertEquals(28, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); assertEquals(10, outcome.getRecordsProcessed()); runInTransaction(()-> assertEquals(0, myResourceTableDao.count())); } @@ -1037,10 +1026,10 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test @ParameterizedTest @CsvSource({ // OptimisticLock OptimizeMode ExpectedSelect ExpectedUpdate - " false, CURRENT_VERSION, 2, 0", - " true, CURRENT_VERSION, 12, 0", - " false, ALL_VERSIONS, 12, 0", - " true, ALL_VERSIONS, 22, 0", + " false, CURRENT_VERSION, 1, 0", + " true, CURRENT_VERSION, 11, 0", + " false, ALL_VERSIONS, 11, 0", + " true, ALL_VERSIONS, 21, 0", }) public void testReindexJob_OptimizeStorage(boolean theOptimisticLock, ReindexParameters.OptimizeStorageModeEnum theOptimizeStorageModeEnum, int theExpectedSelectCount, int theExpectedUpdateCount) { // Setup @@ -1839,7 +1828,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(1, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); - assertEquals(21, myCaptureQueriesListener.countInsertQueries()); + assertEquals(18, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -1852,7 +1841,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(4, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(2, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -1868,7 +1857,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(4, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(2, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -1925,7 +1914,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test // Search for IDs and Search for tag definition assertEquals(3, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); - assertEquals(29, myCaptureQueriesListener.countInsertQueries()); + assertEquals(26, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -1938,7 +1927,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(9, myCaptureQueriesListener.countSelectQueries()); + assertEquals(7, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(7, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -1954,7 +1943,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(7, myCaptureQueriesListener.countSelectQueries()); + assertEquals(5, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(5, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -2250,7 +2239,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(9, myCaptureQueriesListener.countSelectQueries()); + assertEquals(8, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(4, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -2267,7 +2256,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(8, myCaptureQueriesListener.countSelectQueries()); + assertEquals(7, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(4, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -2282,7 +2271,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueries(); - assertEquals(6, myCaptureQueriesListener.countSelectQueries()); + assertEquals(5, myCaptureQueriesListener.countSelectQueries()); myCaptureQueriesListener.logInsertQueries(); assertEquals(4, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); @@ -2453,7 +2442,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); - assertEquals(7, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(6, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -3000,7 +2989,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(17, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(6607, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(6189, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(418, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); assertEquals(2, myCaptureQueriesListener.countCommits()); @@ -3368,7 +3357,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); mySystemDao.transaction(new SystemRequestDetails(), supplier.get()); assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(30, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(29, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -3376,7 +3365,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); Bundle outcome = mySystemDao.transaction(new SystemRequestDetails(), supplier.get()); - assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueries(); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(6, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); @@ -3399,7 +3388,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); outcome = mySystemDao.transaction(new SystemRequestDetails(), supplier.get()); - assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueries(); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(6, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); @@ -3451,7 +3440,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test mySystemDao.transaction(new SystemRequestDetails(), loadResourceFromClasspath(Bundle.class, "r4/transaction-perf-bundle.json")); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(125, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(120, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -3460,7 +3449,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.clear(); mySystemDao.transaction(new SystemRequestDetails(), loadResourceFromClasspath(Bundle.class, "r4/transaction-perf-bundle-smallchanges.json")); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(2, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(5, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index 1e15bc49293..a329330178d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -31,9 +31,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.test.utilities.ProxyUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.checkerframework.checker.units.qual.A; import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BodyStructure; import org.hl7.fhir.r4.model.CodeableConcept; @@ -1238,12 +1236,11 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { myPatientDao.update(p).getId().toUnqualifiedVersionless(); assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(1, myResourceHistoryTableDao.count()); - assertEquals(1, myForcedIdDao.count()); assertEquals(1, myResourceIndexedSearchParamTokenDao.count()); }); @@ -1264,7 +1261,6 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(2, myResourceHistoryTableDao.count()); - assertEquals(1, myForcedIdDao.count()); assertEquals(1, myResourceIndexedSearchParamTokenDao.count()); }); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java index 42004dba52f..07c474b0ff0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java @@ -15,7 +15,6 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag; @@ -589,13 +588,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.update(p, mySrd); runInTransaction(() -> { - // HFJ_FORCED_ID - List forcedIds = myForcedIdDao.findAll(); - assertEquals(2, forcedIds.size()); - assertEquals(myPartitionId, forcedIds.get(0).getPartitionId().getPartitionId().intValue()); - assertLocalDateFromDbMatches(myPartitionDate, forcedIds.get(0).getPartitionId().getPartitionDate()); - assertEquals(myPartitionId, forcedIds.get(1).getPartitionId().getPartitionId().intValue()); - assertLocalDateFromDbMatches(myPartitionDate, forcedIds.get(1).getPartitionId().getPartitionDate()); + ResourceTable orgResourceTable = myResourceTableDao.findByTypeAndFhirId("Organization", "org").orElseThrow(IllegalArgumentException::new); + assertEquals(myPartitionId, orgResourceTable.getPartitionId().getPartitionId().intValue()); + assertLocalDateFromDbMatches(myPartitionDate, orgResourceTable.getPartitionId().getPartitionDate()); + + ResourceTable patientResourceTable = myResourceTableDao.findByTypeAndFhirId("Patient", "pat").orElseThrow(IllegalArgumentException::new); + assertEquals(myPartitionId, patientResourceTable.getPartitionId().getPartitionId().intValue()); + assertLocalDateFromDbMatches(myPartitionDate, patientResourceTable.getPartitionId().getPartitionDate()); }); } @@ -615,11 +614,11 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.update(p, mySrd); runInTransaction(() -> { - // HFJ_FORCED_ID - List forcedIds = myForcedIdDao.findAll(); - assertEquals(2, forcedIds.size()); - assertEquals(null, forcedIds.get(0).getPartitionId()); - assertEquals(null, forcedIds.get(1).getPartitionId()); + ResourceTable orgResourceTable = myResourceTableDao.findByTypeAndFhirId("Organization", "org").orElseThrow(IllegalArgumentException::new); + assertNull(orgResourceTable.getPartitionId()); + + ResourceTable patientResourceTable = myResourceTableDao.findByTypeAndFhirId("Patient", "pat").orElseThrow(IllegalArgumentException::new); + assertNull(patientResourceTable.getPartitionId()); }); } @@ -639,13 +638,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myPatientDao.update(p, mySrd); runInTransaction(() -> { - // HFJ_FORCED_ID - List forcedIds = myForcedIdDao.findAll(); - assertEquals(2, forcedIds.size()); - assertEquals(null, forcedIds.get(0).getPartitionId().getPartitionId()); - assertLocalDateFromDbMatches(myPartitionDate, forcedIds.get(0).getPartitionId().getPartitionDate()); - assertEquals(null, forcedIds.get(1).getPartitionId().getPartitionId()); - assertLocalDateFromDbMatches(myPartitionDate, forcedIds.get(1).getPartitionId().getPartitionDate()); + ResourceTable orgResourceTable = myResourceTableDao.findByTypeAndFhirId("Organization", "org").orElseThrow(IllegalArgumentException::new); + assertNull(orgResourceTable.getPartitionId().getPartitionId()); + assertLocalDateFromDbMatches(myPartitionDate, orgResourceTable.getPartitionId().getPartitionDate()); + + ResourceTable patientResourceTable = myResourceTableDao.findByTypeAndFhirId("Patient", "pat").orElseThrow(IllegalArgumentException::new); + assertNull(patientResourceTable.getPartitionId().getPartitionId()); + assertLocalDateFromDbMatches(myPartitionDate, patientResourceTable.getPartitionId().getPartitionDate()); }); } @@ -876,8 +875,8 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,")); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID")); } { addReadAllPartitions(); @@ -888,8 +887,8 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,")); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID")); } } @@ -910,7 +909,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID='1'"), searchSql); } @@ -954,7 +953,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -967,7 +966,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1008,7 +1007,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -1022,7 +1021,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1064,7 +1063,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null")); } @@ -2843,7 +2842,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(9, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); @@ -2860,7 +2859,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(7, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); @@ -2875,7 +2874,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { outcome = mySystemDao.transaction(mySrd, input.get()); ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(5, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); @@ -2899,7 +2898,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { myCaptureQueriesListener.logSelectQueries(); assertEquals(18, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(6607, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(6189, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(418, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); assertEquals(2, myCaptureQueriesListener.countCommits()); @@ -2925,7 +2924,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { output = mySystemDao.transaction(requestDetails, input); myCaptureQueriesListener.logSelectQueries(); - assertEquals(29, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(26, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java index 7d428f9c67b..1bb39a2816b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java @@ -27,8 +27,6 @@ import ca.uhn.fhir.util.MultimapCollector; import com.google.common.base.Charsets; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.client.methods.CloseableHttpResponse; @@ -47,7 +45,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.SpyBean; import java.io.IOException; import java.util.List; @@ -64,8 +61,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; public class PatientIdPartitionInterceptorTest extends BaseResourceProviderR4Test { public static final int ALTERNATE_DEFAULT_ID = -1; @@ -355,7 +350,6 @@ public class PatientIdPartitionInterceptorTest extends BaseResourceProviderR4Tes org.setName("name 2"); logAllResources(); - logAllForcedIds(); myOrganizationDao.update(org); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java index 49a9b96a5a2..87b8c10b1d8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java @@ -18,16 +18,15 @@ import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; @@ -379,7 +378,6 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { runInTransaction(() -> assertThat(myResourceTableDao.findAll(), not(empty()))); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), not(empty()))); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), not(empty()))); myPatientDao.expunge(new ExpungeOptions() .setExpungeDeletedResources(true) @@ -387,7 +385,6 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); } @@ -409,7 +406,6 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { runInTransaction(() -> assertThat(myResourceTableDao.findAll(), not(empty()))); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), not(empty()))); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), not(empty()))); // Test myCaptureQueriesListener.clear(); @@ -421,11 +417,10 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { assertEquals(8, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); - assertEquals(9, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + assertEquals(8, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); } @@ -749,7 +744,6 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { .setExpungeOldVersions(true), null); runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); // Create again with the same forced ID p = new Patient(); @@ -788,7 +782,6 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { .setExpungeOldVersions(true), null); runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); - runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); } @@ -1061,6 +1054,5 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { assertThat(myTermConceptDao.findAll(), empty()); assertThat(myResourceTableDao.findAll(), empty()); assertThat(myResourceHistoryTableDao.findAll(), empty()); - assertThat(myForcedIdDao.findAll(), empty()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderLanguageParamR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderLanguageParamR4Test.java new file mode 100644 index 00000000000..6d89e6b7564 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderLanguageParamR4Test.java @@ -0,0 +1,67 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ResourceProviderLanguageParamR4Test extends BaseResourceProviderR4Test { + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamEnabled() { + myStorageSettings.setLanguageSearchParameterEnabled(true); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage("en"); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage("fr"); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + List foundResources; + Bundle result; + + result = myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + + foundResources = toUnqualifiedVersionlessIdValues(result); + assertThat(foundResources, contains(patId.getValue())); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamDisabled() { + myStorageSettings.setLanguageSearchParameterEnabled(new JpaStorageSettings().isLanguageSearchParameterEnabled()); + mySearchParamRegistry.forceRefresh(); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + }); + assertThat(exception.getMessage(), containsString(Msg.code(1223))); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java index 99f8296c05e..1dcca3249da 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java @@ -307,6 +307,86 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test assertFalse(hasParameterByName(respParams, "match")); } + @Test + public void testTranslateByCodeSystemsAndSourceCodeMappedToCodelessTarget() { + // ensure that the current behaviour when a target does not have a code is preserved, and no matches returned + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); + + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_4)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("code").setValue(new CodeType("89012")); + + ourLog.debug("Request Parameters:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.debug("Response Parameters\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertFalse(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("No Matches found", ((StringType) param.getValue()).getValueAsString()); + + assertFalse(hasParameterByName(respParams, "match")); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeWithEquivalenceUnmatched() { + // the equivalence code 'unmatched' is an exception - it does not normally have a target code, + // so it will be included in the collection of matches even if there is no code present + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); + + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_4)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("code").setValue(new CodeType("89123")); + + ourLog.debug("Request Parameters:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.debug("Response Parameters\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertFalse(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Only negative matches found", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + + param = getParameterByName(respParams, "match"); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("unmatched", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertNull(coding.getCode()); + assertNull(coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test public void testTranslateUsingPredicatesWithCodeOnly() { ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java index 51632c5c249..5cabad60eb3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java @@ -346,7 +346,7 @@ public class ServerCapabilityStatementProviderJpaR4Test extends BaseResourceProv CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute(); for (CapabilityStatement.CapabilityStatementRestResourceComponent nextResource : cs.getRestFirstRep().getResource()) { for (CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent nextSp : nextResource.getSearchParam()) { - if (nextSp.getName().equals("_has") || nextSp.getName().equals("_list")) { + if (nextSp.getName().equals("_has") || nextSp.getName().equals("_list") || nextSp.getName().equals("_language")) { if (nextSp.getDefinition() == null) { continue; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexStepTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexStepTest.java index 81ad6c198c7..c619cc0b410 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexStepTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexStepTest.java @@ -64,7 +64,7 @@ public class ReindexStepTest extends BaseJpaR4Test { // Verify assertEquals(2, outcome.getRecordsProcessed()); - assertEquals(6, myCaptureQueriesListener.logSelectQueries().size()); + assertEquals(5, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); myCaptureQueriesListener.logUpdateQueries(); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -95,7 +95,7 @@ public class ReindexStepTest extends BaseJpaR4Test { // Verify assertEquals(2, outcome.getRecordsProcessed()); - assertEquals(8, myCaptureQueriesListener.logSelectQueries().size()); + assertEquals(7, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -128,7 +128,7 @@ public class ReindexStepTest extends BaseJpaR4Test { // Verify assertEquals(2, outcome.getRecordsProcessed()); - assertEquals(6, myCaptureQueriesListener.logSelectQueries().size()); + assertEquals(5, myCaptureQueriesListener.logSelectQueries().size()); // name, family, phonetic, deceased, active assertEquals(5, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -196,7 +196,7 @@ public class ReindexStepTest extends BaseJpaR4Test { // Verify assertEquals(2, outcome.getRecordsProcessed()); - assertEquals(10, myCaptureQueriesListener.logSelectQueries().size()); + assertEquals(9, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(4, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -241,7 +241,7 @@ public class ReindexStepTest extends BaseJpaR4Test { // Verify assertEquals(4, outcome.getRecordsProcessed()); - assertEquals(9, myCaptureQueriesListener.logSelectQueries().size()); + assertEquals(8, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(5, myCaptureQueriesListener.countInsertQueries()); assertEquals(2, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java index a4a18667b1c..7f49edb2daf 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java @@ -18,8 +18,6 @@ import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -27,11 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionTemplate; -import jakarta.annotation.Nonnull; import java.util.Collections; import java.util.List; @@ -73,119 +67,130 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test { @Test public void testByCodeSystemsAndSourceCodeOneToMany() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345"); - translationRequest.setTargetSystem(CS_URL_3); + runInTransaction(() -> { + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(CS_URL_3); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testByCodeSystemsAndSourceCodeOneToOne() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345"); - translationRequest.setTargetSystem(CS_URL_2); + runInTransaction(() -> { + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(CS_URL_2); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("ConceptMap.group.element.target:\n" + target.toString()); + ourLog.info("ConceptMap.group.element.target:\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testByCodeSystemsAndSourceCodeUnmapped() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("BOGUS"); - translationRequest.setTargetSystem(CS_URL_3); + runInTransaction(() -> { + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("BOGUS"); + translationRequest.setTargetSystem(CS_URL_3); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertTrue(targets.isEmpty()); - } + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertTrue(targets.isEmpty()); + }); + } + + @Test + public void testByCodeSystemsAndSourceCodeMatchedWithoutCode() { + createAndPersistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); + + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + runInTransaction(() -> { + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_4) + .setCode("89012"); + translationRequest.setTargetSystem(CS_URL_3); + + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertTrue(targets.isEmpty()); }); } @@ -208,7 +213,7 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test { .addTarget() .setCode("999"); - myConceptMapDao.create(conceptMap); + myConceptMapDao.create(conceptMap, mySrd); TranslationRequest translationRequest = new TranslationRequest() .addCode(CS_URL, "12345") @@ -222,954 +227,903 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test { @Test public void testUsingPredicatesWithCodeOnly() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("12345"); + runInTransaction(() -> { + /* + * Provided: + * source code + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(2); + target = targets.get(2); - ourLog.info("target(2):\n" + target.toString()); + ourLog.info("target(2):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceAndTargetSystem2() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * target code system #2 - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345"); - translationRequest.setTargetSystem(CS_URL_2); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * target code system #2 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(CS_URL_2); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target:\n" + target.toString()); + ourLog.info("target:\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceAndTargetSystem3() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * target code system #3 - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345"); - translationRequest.setTargetSystem(CS_URL_3); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * target code system #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(CS_URL_3); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceSystem() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345"); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(2); + target = targets.get(2); - ourLog.info("target(2):\n" + target.toString()); + ourLog.info("target(2):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceSystemAndVersion1() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * source code system version #1 - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345") - .setVersion("Version 1"); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * source code system version #1 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 1"); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target:\n" + target.toString()); + ourLog.info("target:\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(1, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceSystemAndVersion3() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * source code system version #3 - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL) - .setCode("12345") - .setVersion("Version 3"); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * source code system version #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 3"); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(2, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithSourceValueSet() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source value set - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("12345"); - translationRequest.setSource(VS_URL); + runInTransaction(() -> { + /* + * Provided: + * source code + * source value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setSource(VS_URL); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(2); + target = targets.get(2); - ourLog.info("target(2):\n" + target.toString()); + ourLog.info("target(2):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testUsingPredicatesWithTargetValueSet() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * target value set - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("12345"); - translationRequest.setTarget(VS_URL_2); + runInTransaction(() -> { + /* + * Provided: + * source code + * target value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setTarget(VS_URL_2); - List targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); + List targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - TranslateConceptResult target = targets.get(0); + TranslateConceptResult target = targets.get(0); - ourLog.info("target(0):\n" + target.toString()); + ourLog.info("target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(1); + target = targets.get(1); - ourLog.info("target(1):\n" + target.toString()); + ourLog.info("target(1):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - target = targets.get(2); + target = targets.get(2); - ourLog.info("target(2):\n" + target.toString()); + ourLog.info("target(2):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); - // Test caching. - targets = myConceptMappingSvc.translate(translationRequest).getResults(); - assertNotNull(targets); - assertEquals(3, targets.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); - } + // Test caching. + targets = myConceptMappingSvc.translate(translationRequest).getResults(); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationCache()); }); } @Test public void testWithReverse() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * target code system - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_2) - .setCode("34567"); - translationRequest.setTargetSystem(CS_URL_4); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * target code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(CS_URL_4); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseByCodeSystemsAndSourceCodeUnmapped() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_3) - .setCode("BOGUS"); - translationRequest.setTargetSystem(CS_URL); + runInTransaction(() -> { + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_3) + .setCode("BOGUS"); + translationRequest.setTargetSystem(CS_URL); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertTrue(elements.isEmpty()); - } + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertTrue(elements.isEmpty()); }); } @Test public void testWithReverseUsingPredicatesWithCodeOnly() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("34567"); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - element = elements.getResults().get(1); + element = elements.getResults().get(1); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithSourceAndTargetSystem1() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * target code system #1 - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_2) - .setCode("34567"); - translationRequest.setTargetSystem(CS_URL); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * target code system #1 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(CS_URL); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithSourceAndTargetSystem4() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * target code system #4 - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_2) - .setCode("34567"); - translationRequest.setTargetSystem(CS_URL_4); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * target code system #4 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(CS_URL_4); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(1, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithSourceSystem() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_2) - .setCode("34567"); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - element = elements.getResults().get(1); + element = elements.getResults().get(1); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithSourceSystemAndVersion() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source code system - * source code system version - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setSystem(CS_URL_2) - .setCode("34567") - .setVersion("Version 2"); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567") + .setVersion("Version 2"); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - element = elements.getResults().get(1); + element = elements.getResults().get(1); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithSourceValueSet() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * source value set - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("34567"); - translationRequest.setSource(VS_URL_2); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * source value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setSource(VS_URL_2); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - element = elements.getResults().get(1); + element = elements.getResults().get(1); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @Test public void testWithReverseUsingPredicatesWithTargetValueSet() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId, mySrd); ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - /* - * Provided: - * source code - * target value set - * reverse = true - */ - TranslationRequest translationRequest = new TranslationRequest(); - translationRequest.getCodeableConcept().addCoding() - .setCode("34567"); - translationRequest.setTarget(VS_URL); - translationRequest.setReverse(true); + runInTransaction(() -> { + /* + * Provided: + * source code + * target value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setTarget(VS_URL); + translationRequest.setReverse(true); - TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + TranslateConceptResults elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - TranslateConceptResult element = elements.getResults().get(0); + TranslateConceptResult element = elements.getResults().get(0); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - element = elements.getResults().get(1); + element = elements.getResults().get(1); - ourLog.info("element:\n" + element.toString()); + ourLog.info("element:\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); - // Test caching. - elements = myConceptMappingSvc.translateWithReverse(translationRequest); - assertNotNull(elements); - assertEquals(2, elements.size()); - assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); - } + // Test caching. + elements = myConceptMappingSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(TermConceptMappingSvcImpl.isOurLastResultsFromTranslationWithReverseCache()); }); } @@ -1203,358 +1157,316 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test { @Test public void testStoreTermConceptMapAndChildren() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap originalConceptMap = myConceptMapDao.read(myConceptMapId, mySrd); - ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(originalConceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); - assertEquals(1, optionalConceptMap.size()); + runInTransaction(() -> { + Pageable page = PageRequest.of(0, 1); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(0); + TermConceptMap conceptMap = optionalConceptMap.get(0); - ourLog.info("ConceptMap:\n" + conceptMap.toString()); + ourLog.info("ConceptMap:\n" + conceptMap.toString()); - assertEquals(VS_URL, conceptMap.getSource()); - assertEquals(VS_URL_2, conceptMap.getTarget()); - assertEquals(CM_URL, conceptMap.getUrl()); - assertEquals(3, conceptMap.getConceptMapGroups().size()); + assertEquals(VS_URL, conceptMap.getSource()); + assertEquals(VS_URL_2, conceptMap.getTarget()); + assertEquals(CM_URL, conceptMap.getUrl()); + assertEquals(4, conceptMap.getConceptMapGroups().size()); - TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(0); + TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(0); - ourLog.info("ConceptMap.group(0):\n" + group.toString()); + ourLog.info("ConceptMap.group(0):\n" + group.toString()); - assertEquals(CS_URL, group.getSource()); - assertEquals("Version 1", group.getSourceVersion()); - assertEquals(VS_URL, group.getSourceValueSet()); - assertEquals(CS_URL_2, group.getTarget()); - assertEquals("Version 2", group.getTargetVersion()); - assertEquals(VS_URL_2, group.getTargetValueSet()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(2, group.getConceptMapGroupElements().size()); + assertGroupHasValues( + CS_URL,"Version 1", CS_URL_2, "Version 2", group); + assertEquals(VS_URL, group.getSourceValueSet()); + assertEquals(VS_URL_2, group.getTargetValueSet()); + assertEquals(2, group.getConceptMapGroupElements().size()); - TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); + TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(0).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(0).element(0):\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(1, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues( + "12345", "Source Code 12345", CS_URL, "Version 1", element); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); - TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); + TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(0).element(0).target(0):\n" + target.toString()); + ourLog.info("ConceptMap.group(0).element(0).target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues( + "34567", "Target Code 34567", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.EQUAL, target); - element = group.getConceptMapGroupElements().get(1); + element = group.getConceptMapGroupElements().get(1); - ourLog.info("ConceptMap.group(0).element(1):\n" + element.toString()); + ourLog.info("ConceptMap.group(0).element(1):\n" + element.toString()); - assertEquals("23456", element.getCode()); - assertEquals("Source Code 23456", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertElementHasValues( + "23456", "Source Code 23456", CS_URL, "Version 1", element); - assertEquals(2, element.getConceptMapGroupElementTargets().size()); + assertEquals(2, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(0).element(1).target(0):\n" + target.toString()); - assertEquals("45678", target.getCode()); - assertEquals("Target Code 45678", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + target = element.getConceptMapGroupElementTargets().get(0); + ourLog.info("ConceptMap.group(0).element(1).target(0):\n" + target.toString()); + assertTargetHasValues( + "45678", "Target Code 45678", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.WIDER, target); - // We had deliberately added a duplicate, and here it is... - target = element.getConceptMapGroupElementTargets().get(1); - ourLog.info("ConceptMap.group(0).element(1).target(1):\n" + target.toString()); - assertEquals("45678", target.getCode()); - assertEquals("Target Code 45678", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + // We had deliberately added a duplicate, and here it is... + target = element.getConceptMapGroupElementTargets().get(1); + ourLog.info("ConceptMap.group(0).element(1).target(1):\n" + target.toString()); + assertTargetHasValues( + "45678", "Target Code 45678", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.WIDER, target); - group = conceptMap.getConceptMapGroups().get(1); + group = conceptMap.getConceptMapGroups().get(1); - ourLog.info("ConceptMap.group(1):\n" + group.toString()); + ourLog.info("ConceptMap.group(1):\n" + group.toString()); - assertEquals(CS_URL, group.getSource()); - assertEquals("Version 3", group.getSourceVersion()); - assertEquals(CS_URL_3, group.getTarget()); - assertEquals("Version 4", group.getTargetVersion()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(1, group.getConceptMapGroupElements().size()); + assertGroupHasValues( + CS_URL, "Version 3", CS_URL_3, "Version 4", group); + assertEquals(1, group.getConceptMapGroupElements().size()); - element = group.getConceptMapGroupElements().get(0); + element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(1).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(1).element(0):\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 3", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(2, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues( + "12345", "Source Code 12345", CS_URL, "Version 3", element); + assertEquals(2, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); + target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(1).element(0).target(0):\n" + target.toString()); + ourLog.info("ConceptMap.group(1).element(0).target(0):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues( + "56789", "Target Code 56789", CS_URL_3, "Version 4", Enumerations.ConceptMapEquivalence.EQUAL, target); - target = element.getConceptMapGroupElementTargets().get(1); + target = element.getConceptMapGroupElementTargets().get(1); - ourLog.info("ConceptMap.group(1).element(0).target(1):\n" + target.toString()); + ourLog.info("ConceptMap.group(1).element(0).target(1):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues( + "67890", "Target Code 67890", CS_URL_3, "Version 4", Enumerations.ConceptMapEquivalence.WIDER, target); - group = conceptMap.getConceptMapGroups().get(2); + group = conceptMap.getConceptMapGroups().get(2); - ourLog.info("ConceptMap.group(2):\n" + group.toString()); + ourLog.info("ConceptMap.group(2):\n" + group.toString()); - assertEquals(CS_URL_4, group.getSource()); - assertEquals("Version 5", group.getSourceVersion()); - assertEquals(CS_URL_2, group.getTarget()); - assertEquals("Version 2", group.getTargetVersion()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(1, group.getConceptMapGroupElements().size()); + assertGroupHasValues( + CS_URL_4, "Version 5", CS_URL_2, "Version 2", group); + assertEquals(1, group.getConceptMapGroupElements().size()); - element = group.getConceptMapGroupElements().get(0); + element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(2).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(2).element(0):\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(1, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues( + "78901", "Source Code 78901", CS_URL_4, "Version 5", element); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); + target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(2).element(0).target(0):\n" + target.toString()); - - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.NARROWER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); - } + ourLog.info("ConceptMap.group(2).element(0).target(0):\n" + target.toString()); }); } + @Test + public void testStoreTermConceptMapAndChildren_handleUnmappedElements() { + createAndPersistConceptMap(); + ConceptMap originalConceptMap = myConceptMapDao.read(myConceptMapId, mySrd); + + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(originalConceptMap)); + + runInTransaction(() -> { + Pageable page = PageRequest.of(0, 1); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); + + TermConceptMap conceptMap = optionalConceptMap.get(0); + TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(3); + + ourLog.info("ConceptMap.group(3):\n" + group.toString()); + + assertGroupHasValues( + CS_URL_4, "Version 1", CS_URL_3, "Version 1", group); + assertEquals(2, group.getConceptMapGroupElements().size()); + + TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); + + ourLog.info("ConceptMap.group(3).element(0):\n" + element.toString()); + + assertElementHasValues( + "89012", "Source Code 89012", CS_URL_4, "Version 1", element); + assertEquals(0, element.getConceptMapGroupElementTargets().size()); + + element = group.getConceptMapGroupElements().get(1); + + ourLog.info("ConceptMap.group(3).element(1):\n" + element.toString()); + + assertElementHasValues( + "89123", "Source Code 89123", CS_URL_4, "Version 1", element); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); + + TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); + + ourLog.info("ConceptMap.group(3).element(1).target(0):\n" + target.toString()); + + assertTargetHasValues( + null, null, CS_URL_3, "Version 1", Enumerations.ConceptMapEquivalence.UNMATCHED, target); + }); + } + + private void assertGroupHasValues(String theExpectedSourceCodeSystem, + String theExpectedSourceCodeSystemVersion, + String theExpectedTargetCodeSystem, + String theExpectedTargetCodeSystemVersion, + TermConceptMapGroup group) { + assertEquals(theExpectedSourceCodeSystem, group.getSource()); + assertEquals(theExpectedSourceCodeSystemVersion, group.getSourceVersion()); + assertEquals(theExpectedTargetCodeSystem, group.getTarget()); + assertEquals(theExpectedTargetCodeSystemVersion, group.getTargetVersion()); + assertEquals(CM_URL, group.getConceptMapUrl()); + } + + private void assertElementHasValues(String theExpectedCode, + String theExpectedDisplayText, + String theExpectedCodeSystem, + String theExpectedCodeSystemVersion, + TermConceptMapGroupElement element) { + assertEquals(theExpectedCode, element.getCode()); + assertEquals(theExpectedDisplayText, element.getDisplay()); + assertEquals(theExpectedCodeSystem, element.getSystem()); + assertEquals(theExpectedCodeSystemVersion, element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + } + + private void assertTargetHasValues(String theExpectedCode, + String theExpectedDisplayText, + String theExpectedCodeSystem, + String theExpectedCodeSystemVersion, + Enumerations.ConceptMapEquivalence theExpectedEquivalence, + TermConceptMapGroupElementTarget target) { + assertEquals(theExpectedCode, target.getCode()); + assertEquals(theExpectedDisplayText, target.getDisplay()); + assertEquals(theExpectedCodeSystem, target.getSystem()); + assertEquals(theExpectedCodeSystemVersion, target.getSystemVersion()); + assertEquals(theExpectedEquivalence, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + } + @Test public void testStoreTermConceptMapAndChildrenWithClientAssignedId() { createAndPersistConceptMap(); - ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + ConceptMap originalConceptMap = myConceptMapDao.read(myConceptMapId, mySrd); - ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + ourLog.debug("ConceptMap:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(originalConceptMap)); - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); - assertEquals(1, optionalConceptMap.size()); + runInTransaction(() -> { + Pageable page = PageRequest.of(0, 1); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(0); + TermConceptMap conceptMap = optionalConceptMap.get(0); - ourLog.info("ConceptMap:\n" + conceptMap.toString()); + ourLog.info("ConceptMap:\n" + conceptMap.toString()); - assertEquals(VS_URL, conceptMap.getSource()); - assertEquals(VS_URL_2, conceptMap.getTarget()); - assertEquals(CM_URL, conceptMap.getUrl()); - assertEquals(3, conceptMap.getConceptMapGroups().size()); + assertEquals(VS_URL, conceptMap.getSource()); + assertEquals(VS_URL_2, conceptMap.getTarget()); + assertEquals(CM_URL, conceptMap.getUrl()); + assertEquals(4, conceptMap.getConceptMapGroups().size()); - TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(0); + TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(0); - ourLog.info("ConceptMap.group(0):\n" + group.toString()); + ourLog.info("ConceptMap.group(0):\n" + group.toString()); - assertEquals(CS_URL, group.getSource()); - assertEquals("Version 1", group.getSourceVersion()); - assertEquals(VS_URL, group.getSourceValueSet()); - assertEquals(CS_URL_2, group.getTarget()); - assertEquals("Version 2", group.getTargetVersion()); - assertEquals(VS_URL_2, group.getTargetValueSet()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(2, group.getConceptMapGroupElements().size()); + assertEquals(CS_URL, group.getSource()); + assertEquals("Version 1", group.getSourceVersion()); + assertEquals(VS_URL, group.getSourceValueSet()); + assertEquals(CS_URL_2, group.getTarget()); + assertEquals("Version 2", group.getTargetVersion()); + assertEquals(VS_URL_2, group.getTargetValueSet()); + assertEquals(CM_URL, group.getConceptMapUrl()); + assertEquals(2, group.getConceptMapGroupElements().size()); - TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); + TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(0).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(0).element(0):\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(1, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues("12345", "Source Code 12345", CS_URL, "Version 1", element); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); - TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); + TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(0).element(0).target(0):\n" + target.toString()); + ourLog.info("ConceptMap.group(0).element(0).target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues("34567", "Target Code 34567", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.EQUAL, target); - element = group.getConceptMapGroupElements().get(1); + element = group.getConceptMapGroupElements().get(1); - ourLog.info("ConceptMap.group(0).element(1):\n" + element.toString()); + ourLog.info("ConceptMap.group(0).element(1):\n" + element.toString()); - assertEquals("23456", element.getCode()); - assertEquals("Source Code 23456", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 1", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); + assertElementHasValues("23456", "Source Code 23456", CS_URL, "Version 1", element); - assertEquals(2, element.getConceptMapGroupElementTargets().size()); + assertEquals(2, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(0).element(1).target(0):\n" + target.toString()); - assertEquals("45678", target.getCode()); - assertEquals("Target Code 45678", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + target = element.getConceptMapGroupElementTargets().get(0); + ourLog.info("ConceptMap.group(0).element(1).target(0):\n" + target.toString()); + assertTargetHasValues("45678", "Target Code 45678", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.WIDER, target); - // We had deliberately added a duplicate, and here it is... - target = element.getConceptMapGroupElementTargets().get(1); - ourLog.info("ConceptMap.group(0).element(1).target(1):\n" + target.toString()); - assertEquals("45678", target.getCode()); - assertEquals("Target Code 45678", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + // We had deliberately added a duplicate, and here it is... + target = element.getConceptMapGroupElementTargets().get(1); + ourLog.info("ConceptMap.group(0).element(1).target(1):\n" + target.toString()); + assertTargetHasValues("45678", "Target Code 45678", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.WIDER, target); - group = conceptMap.getConceptMapGroups().get(1); + group = conceptMap.getConceptMapGroups().get(1); - ourLog.info("ConceptMap.group(1):\n" + group.toString()); + ourLog.info("ConceptMap.group(1):\n" + group.toString()); - assertEquals(CS_URL, group.getSource()); - assertEquals("Version 3", group.getSourceVersion()); - assertEquals(CS_URL_3, group.getTarget()); - assertEquals("Version 4", group.getTargetVersion()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(1, group.getConceptMapGroupElements().size()); + assertGroupHasValues(CS_URL, "Version 3", CS_URL_3, "Version 4", group); + assertEquals(1, group.getConceptMapGroupElements().size()); - element = group.getConceptMapGroupElements().get(0); + element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(1).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(1).element(0):\n" + element.toString()); - assertEquals("12345", element.getCode()); - assertEquals("Source Code 12345", element.getDisplay()); - assertEquals(CS_URL, element.getSystem()); - assertEquals("Version 3", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(2, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues("12345", "Source Code 12345", CS_URL, "Version 3", element); + assertEquals(2, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); + target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(1).element(0).target(0):\n" + target.toString()); + ourLog.info("ConceptMap.group(1).element(0).target(0):\n" + target.toString()); - assertEquals("56789", target.getCode()); - assertEquals("Target Code 56789", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.EQUAL, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues("56789", "Target Code 56789", CS_URL_3, "Version 4", Enumerations.ConceptMapEquivalence.EQUAL, target); - target = element.getConceptMapGroupElementTargets().get(1); + target = element.getConceptMapGroupElementTargets().get(1); - ourLog.info("ConceptMap.group(1).element(0).target(1):\n" + target.toString()); + ourLog.info("ConceptMap.group(1).element(0).target(1):\n" + target.toString()); - assertEquals("67890", target.getCode()); - assertEquals("Target Code 67890", target.getDisplay()); - assertEquals(CS_URL_3, target.getSystem()); - assertEquals("Version 4", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.WIDER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); + assertTargetHasValues("67890", "Target Code 67890", CS_URL_3, "Version 4", Enumerations.ConceptMapEquivalence.WIDER, target); - group = conceptMap.getConceptMapGroups().get(2); + group = conceptMap.getConceptMapGroups().get(2); - ourLog.info("ConceptMap.group(2):\n" + group.toString()); + ourLog.info("ConceptMap.group(2):\n" + group.toString()); - assertEquals(CS_URL_4, group.getSource()); - assertEquals("Version 5", group.getSourceVersion()); - assertEquals(CS_URL_2, group.getTarget()); - assertEquals("Version 2", group.getTargetVersion()); - assertEquals(CM_URL, group.getConceptMapUrl()); - assertEquals(1, group.getConceptMapGroupElements().size()); + assertGroupHasValues(CS_URL_4, "Version 5", CS_URL_2, "Version 2", group); + assertEquals(1, group.getConceptMapGroupElements().size()); - element = group.getConceptMapGroupElements().get(0); + element = group.getConceptMapGroupElements().get(0); - ourLog.info("ConceptMap.group(2).element(0):\n" + element.toString()); + ourLog.info("ConceptMap.group(2).element(0):\n" + element.toString()); - assertEquals("78901", element.getCode()); - assertEquals("Source Code 78901", element.getDisplay()); - assertEquals(CS_URL_4, element.getSystem()); - assertEquals("Version 5", element.getSystemVersion()); - assertEquals(VS_URL, element.getValueSet()); - assertEquals(CM_URL, element.getConceptMapUrl()); - assertEquals(1, element.getConceptMapGroupElementTargets().size()); + assertElementHasValues("78901", "Source Code 78901", CS_URL_4, "Version 5", element); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); - target = element.getConceptMapGroupElementTargets().get(0); + target = element.getConceptMapGroupElementTargets().get(0); - ourLog.info("ConceptMap.group(2).element(0).target(0):\n" + target.toString()); + ourLog.info("ConceptMap.group(2).element(0).target(0):\n" + target.toString()); - assertEquals("34567", target.getCode()); - assertEquals("Target Code 34567", target.getDisplay()); - assertEquals(CS_URL_2, target.getSystem()); - assertEquals("Version 2", target.getSystemVersion()); - assertEquals(Enumerations.ConceptMapEquivalence.NARROWER, target.getEquivalence()); - assertEquals(VS_URL_2, target.getValueSet()); - assertEquals(CM_URL, target.getConceptMapUrl()); - } + assertTargetHasValues("34567", "Target Code 34567", CS_URL_2, "Version 2", Enumerations.ConceptMapEquivalence.NARROWER, target); }); } @@ -1670,19 +1582,13 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test { private void persistConceptMap(ConceptMap theConceptMap, HttpVerb theVerb) { switch (theVerb) { case POST: - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - myConceptMapId = myConceptMapDao.create(theConceptMap, mySrd).getId().toUnqualifiedVersionless(); - } + runInTransaction(() -> { + myConceptMapId = myConceptMapDao.create(theConceptMap, mySrd).getId().toUnqualifiedVersionless(); }); break; case PUT: - new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - myConceptMapId = myConceptMapDao.update(theConceptMap, mySrd).getId().toUnqualifiedVersionless(); - } + runInTransaction(() -> { + myConceptMapId = myConceptMapDao.update(theConceptMap, mySrd).getId().toUnqualifiedVersionless(); }); break; default: diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 77be64734fc..9013e0c6b72 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java index 0a275e83112..afb2d993192 100644 --- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java @@ -18,7 +18,6 @@ import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao; @@ -64,6 +63,7 @@ import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.persistence.EntityManager; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -124,7 +124,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.List; import java.util.Optional; @@ -280,8 +279,6 @@ public abstract class BaseJpaR4BTest extends BaseJpaTest implements ITestDataBui @Autowired protected IResourceHistoryTableDao myResourceHistoryTableDao; @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired @Qualifier("myCoverageDaoR4B") protected IFhirResourceDao myCoverageDao; @Autowired diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderLanguageParamR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderLanguageParamR4BTest.java new file mode 100644 index 00000000000..1d18ea0ce3b --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderLanguageParamR4BTest.java @@ -0,0 +1,66 @@ +package ca.uhn.fhir.jpa.provider.r4b; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Patient; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ResourceProviderLanguageParamR4BTest extends BaseResourceProviderR4BTest { + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamEnabled() { + myStorageSettings.setLanguageSearchParameterEnabled(true); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage("en"); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage("fr"); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + List foundResources; + Bundle result; + + result = myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + + foundResources = toUnqualifiedVersionlessIdValues(result); + assertThat(foundResources, contains(patId.getValue())); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamDisabled() { + myStorageSettings.setLanguageSearchParameterEnabled(new JpaStorageSettings().isLanguageSearchParameterEnabled()); + mySearchParamRegistry.forceRefresh(); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + }); + assertThat(exception.getMessage(), containsString(Msg.code(1223))); + } +} diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 545c9c2fbb4..f91d4266b1a 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index 75a846ccb87..d6c38965bbb 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -19,7 +19,6 @@ import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao; @@ -72,6 +71,7 @@ import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import jakarta.persistence.EntityManager; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.model.AllergyIntolerance; @@ -134,7 +134,6 @@ import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -303,8 +302,6 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil @Autowired protected IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao; @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired @Qualifier("myCoverageDaoR5") protected IFhirResourceDao myCoverageDao; @Autowired diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java index f5d7992b1fc..2fe8d2762ca 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.util.BundleBuilder; +import jakarta.annotation.Nonnull; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CodeType; @@ -20,7 +21,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.UUID; @@ -28,7 +28,6 @@ import static org.apache.commons.lang3.StringUtils.countMatches; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.matchesPattern; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -151,7 +150,7 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { // Verify - assertEquals(theMatchUrlCacheEnabled ? 4 : 5, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(theMatchUrlCacheEnabled ? 3 : 4, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); @@ -203,7 +202,7 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { // Verify - assertEquals(theMatchUrlCacheEnabled ? 4 : 5, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(theMatchUrlCacheEnabled ? 3 : 4, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java index 453b541eebb..5cf0e2a4127 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java @@ -401,7 +401,7 @@ public class UpliftedRefchainsAndChainedSortingR5Test extends BaseJpaR5Test { // 1- Resolve resource forced IDs, and 2- Resolve Practitioner/PR1 reference myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(10, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(9, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); // Verify correct indexes are written @@ -441,7 +441,7 @@ public class UpliftedRefchainsAndChainedSortingR5Test extends BaseJpaR5Test { bb.addTransactionUpdateEntry(newEncounter(ENCOUNTER_E2, p2Id)); bb.addTransactionCreateEntry(newPatientP1_HomerSimpson().setId(p1Id)).conditional("identifier=http://system|200"); bb.addTransactionCreateEntry(newPatientP2_MargeSimpson().setId(p2Id)).conditional("identifier=http://system|300"); - ; + Bundle requestBundle = bb.getBundleTyped(); myCaptureQueriesListener.clear(); @@ -496,7 +496,7 @@ public class UpliftedRefchainsAndChainedSortingR5Test extends BaseJpaR5Test { bb.addTransactionUpdateEntry(newEncounter(ENCOUNTER_E2, p2Id)); bb.addTransactionCreateEntry(new Patient().addIdentifier(new Identifier().setSystem("http://system").setValue("200")).setId(p1Id)).conditional("identifier=http://system|200"); bb.addTransactionCreateEntry(new Patient().addIdentifier(new Identifier().setSystem("http://system").setValue("300")).setId(p2Id)).conditional("identifier=http://system|300"); - ; + Bundle requestBundle = bb.getBundleTyped(); myCaptureQueriesListener.clear(); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderLanguageParamR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderLanguageParamR5Test.java new file mode 100644 index 00000000000..4f0ceb0fc92 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderLanguageParamR5Test.java @@ -0,0 +1,66 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.Patient; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ResourceProviderLanguageParamR5Test extends BaseResourceProviderR5Test { + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamEnabled() { + myStorageSettings.setLanguageSearchParameterEnabled(true); + mySearchParamRegistry.forceRefresh(); + + Patient pat = new Patient(); + pat.setLanguage("en"); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat.setLanguage("fr"); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + List foundResources; + Bundle result; + + result = myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + + foundResources = toUnqualifiedVersionlessIdValues(result); + assertThat(foundResources, contains(patId.getValue())); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithLanguageParamDisabled() { + myStorageSettings.setLanguageSearchParameterEnabled(new JpaStorageSettings().isLanguageSearchParameterEnabled()); + mySearchParamRegistry.forceRefresh(); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + myClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam(Constants.PARAM_LANGUAGE).exactly().code("en")) + .returnBundle(Bundle.class) + .execute(); + }); + assertThat(exception.getMessage(), containsString(Msg.code(1223))); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index d92fba3b123..72fcdec2607 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java index 531291487e4..33d536600c1 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java @@ -20,8 +20,6 @@ package ca.uhn.fhir.jpa.test; import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider; -import ca.uhn.fhir.batch2.jobs.reindex.ReindexAppCtx; -import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.interceptor.api.IInterceptorService; @@ -45,7 +43,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.TestDaoSearch; import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaRepository; import ca.uhn.fhir.jpa.dao.data.IPartitionDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao; @@ -116,6 +113,7 @@ import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.persistence.EntityManager; import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -208,7 +206,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; -import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -935,6 +932,28 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil target.setDisplay("Target Code 34567"); target.setEquivalence(ConceptMapEquivalence.NARROWER); + group = conceptMap.addGroup(); + group.setSource(CS_URL_4); + group.setSourceVersion("Version 1"); + group.setTarget(CS_URL_3); + group.setTargetVersion("Version 1"); + + // This one should not show up in the results because it doesn't have a code + element = group.addElement(); + element.setCode("89012"); + element.setDisplay("Source Code 89012"); + + target = element.addTarget(); + target.setEquivalence(ConceptMapEquivalence.DISJOINT); + + // This one should show up in the results because unmatched targets are allowed to be codeless + element = group.addElement(); + element.setCode("89123"); + element.setDisplay("Source Code 89123"); + + target = element.addTarget(); + target.setEquivalence(ConceptMapEquivalence.UNMATCHED); + return conceptMap; } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java index b0012b2a279..107e77268da 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java @@ -36,7 +36,6 @@ import ca.uhn.fhir.jpa.config.JpaConfig; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.JpaPersistedResourceValidationSupport; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboTokensNonUniqueDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamCoordsDao; @@ -61,7 +60,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptProperty; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; @@ -99,6 +97,8 @@ import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.FhirVersionIndependentConcept; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.TestUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -129,8 +129,6 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import jakarta.annotation.Nonnull; -import jakarta.persistence.EntityManager; import java.io.IOException; import java.time.Duration; import java.time.temporal.ChronoUnit; @@ -257,8 +255,6 @@ public abstract class BaseJpaTest extends BaseTest { @Autowired private IResourceHistoryTableDao myResourceHistoryTableDao; @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired private DaoRegistry myDaoRegistry; private final List myRegisteredInterceptors = new ArrayList<>(1); @@ -517,14 +513,6 @@ public abstract class BaseJpaTest extends BaseTest { }); } - protected int logAllForcedIds() { - return runInTransaction(() -> { - List forcedIds = myForcedIdDao.findAll(); - ourLog.info("Resources:\n * {}", forcedIds.stream().map(ForcedId::toString).collect(Collectors.joining("\n * "))); - return forcedIds.size(); - }); - } - protected void logAllDateIndexes() { runInTransaction(() -> { ourLog.info("Date indexes:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(ResourceIndexedSearchParamDate::toString).collect(Collectors.joining("\n * "))); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/H2_EMBEDDED.sql b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/H2_EMBEDDED.sql new file mode 100644 index 00000000000..e84689abc35 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/H2_EMBEDDED.sql @@ -0,0 +1 @@ +INSERT INTO TRM_CONCEPT_MAP_GRP_ELM_TGT (PID, TARGET_CODE, CONCEPT_MAP_URL, TARGET_DISPLAY, TARGET_EQUIVALENCE, SYSTEM_URL, SYSTEM_VERSION, VALUESET_URL, CONCEPT_MAP_GRP_ELM_PID) VALUES (61, NULL, NULL, 'PYRIDOXINE', 'UNMATCHED', NULL, NULL, NULL, 60); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/MSSQL_2012.sql b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/MSSQL_2012.sql new file mode 100644 index 00000000000..e84689abc35 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/MSSQL_2012.sql @@ -0,0 +1 @@ +INSERT INTO TRM_CONCEPT_MAP_GRP_ELM_TGT (PID, TARGET_CODE, CONCEPT_MAP_URL, TARGET_DISPLAY, TARGET_EQUIVALENCE, SYSTEM_URL, SYSTEM_VERSION, VALUESET_URL, CONCEPT_MAP_GRP_ELM_PID) VALUES (61, NULL, NULL, 'PYRIDOXINE', 'UNMATCHED', NULL, NULL, NULL, 60); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/ORACLE_12C.sql b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/ORACLE_12C.sql new file mode 100644 index 00000000000..e84689abc35 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/ORACLE_12C.sql @@ -0,0 +1 @@ +INSERT INTO TRM_CONCEPT_MAP_GRP_ELM_TGT (PID, TARGET_CODE, CONCEPT_MAP_URL, TARGET_DISPLAY, TARGET_EQUIVALENCE, SYSTEM_URL, SYSTEM_VERSION, VALUESET_URL, CONCEPT_MAP_GRP_ELM_PID) VALUES (61, NULL, NULL, 'PYRIDOXINE', 'UNMATCHED', NULL, NULL, NULL, 60); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/POSTGRES_9_4.sql b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/POSTGRES_9_4.sql new file mode 100644 index 00000000000..e84689abc35 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/resources/migration/releases/V7_2_0/data/POSTGRES_9_4.sql @@ -0,0 +1 @@ +INSERT INTO TRM_CONCEPT_MAP_GRP_ELM_TGT (PID, TARGET_CODE, CONCEPT_MAP_URL, TARGET_DISPLAY, TARGET_EQUIVALENCE, SYSTEM_URL, SYSTEM_VERSION, VALUESET_URL, CONCEPT_MAP_GRP_ELM_PID) VALUES (61, NULL, NULL, 'PYRIDOXINE', 'UNMATCHED', NULL, NULL, NULL, 60); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcTest.java index 322f7f75fb4..427be9fba23 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcTest.java @@ -3,16 +3,17 @@ package ca.uhn.fhir.jpa.cache; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.cache.ResourcePersistentIdMap; -import ca.uhn.fhir.jpa.cache.ResourceVersionSvcDaoImpl; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -23,11 +24,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Path; -import jakarta.persistence.criteria.Root; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -36,7 +32,6 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -85,8 +80,6 @@ public class ResourceVersionSvcTest { CriteriaQuery criteriaQuery = Mockito.mock(CriteriaQuery.class); Root from = Mockito.mock(Root.class); Path path = Mockito.mock(Path.class); - - TypedQuery queryMock = Mockito.mock(TypedQuery.class); } /** diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java index 29642613f88..b5fd1631e5b 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java @@ -83,7 +83,8 @@ public class HapiSchemaMigrationTest { VersionEnum.V5_4_0, VersionEnum.V5_5_0, VersionEnum.V6_0_0, - VersionEnum.V6_6_0 + VersionEnum.V6_6_0, + VersionEnum.V7_2_0 ); int fromVersion = 0; @@ -92,6 +93,7 @@ public class HapiSchemaMigrationTest { for (int i = 0; i < allVersions.length; i++) { toVersion = allVersions[i]; + ourLog.info("Applying migrations for {}", toVersion); migrate(theDriverType, dataSource, hapiMigrationStorageSvc, toVersion); if (dataVersions.contains(toVersion)) { myEmbeddedServersExtension.insertPersistenceTestData(theDriverType, toVersion); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index a38b2b8c988..434fa5b7fd1 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 4749239fb7f..4e2bbda8f5e 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 366ceb47463..5d85b54408b 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java index aa1d32e72a3..8c64b2b898d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java @@ -32,6 +32,8 @@ import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.function.Supplier; + @Service public class MdmProviderLoader { @Autowired @@ -58,26 +60,28 @@ public class MdmProviderLoader { @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; - private BaseMdmProvider myMdmProvider; - private MdmLinkHistoryProviderDstu3Plus myMdmHistoryProvider; + private Supplier myMdmProviderSupplier; + private Supplier myMdmHistoryProviderSupplier; public void loadProvider() { switch (myFhirContext.getVersion().getVersion()) { case DSTU3: case R4: case R5: - myResourceProviderFactory.addSupplier(() -> new MdmProviderDstu3Plus( + // We store the supplier so that removeSupplier works properly + myMdmProviderSupplier = () -> new MdmProviderDstu3Plus( myFhirContext, myMdmControllerSvc, myMdmControllerHelper, myMdmSubmitSvc, myInterceptorBroadcaster, - myMdmSettings)); + myMdmSettings); + // We store the supplier so that removeSupplier works properly + myResourceProviderFactory.addSupplier(myMdmProviderSupplier); if (myStorageSettings.isNonResourceDbHistoryEnabled()) { - myResourceProviderFactory.addSupplier(() -> { - return new MdmLinkHistoryProviderDstu3Plus( - myFhirContext, myMdmControllerSvc, myInterceptorBroadcaster); - }); + myMdmHistoryProviderSupplier = () -> new MdmLinkHistoryProviderDstu3Plus( + myFhirContext, myMdmControllerSvc, myInterceptorBroadcaster); + myResourceProviderFactory.addSupplier(myMdmHistoryProviderSupplier); } break; default: @@ -88,11 +92,11 @@ public class MdmProviderLoader { @PreDestroy public void unloadProvider() { - if (myMdmProvider != null) { - myResourceProviderFactory.removeSupplier(() -> myMdmProvider); + if (myMdmProviderSupplier != null) { + myResourceProviderFactory.removeSupplier(myMdmProviderSupplier); } - if (myMdmHistoryProvider != null) { - myResourceProviderFactory.removeSupplier(() -> myMdmHistoryProvider); + if (myMdmHistoryProviderSupplier != null) { + myResourceProviderFactory.removeSupplier(myMdmHistoryProviderSupplier); } } } diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index cfaae68b9d9..6371abe0514 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 3d74ccc0870..03bba016e4a 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IObservableSupplierSetObserver.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IObservableSupplierSetObserver.java new file mode 100644 index 00000000000..90bed1f2ada --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IObservableSupplierSetObserver.java @@ -0,0 +1,33 @@ +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.rest.server.provider; + +import jakarta.annotation.Nonnull; + +import java.util.function.Supplier; + +/** + * See {@link ObservableSupplierSet} + */ +public interface IObservableSupplierSetObserver { + void update(@Nonnull Supplier theSupplier); + + void remove(@Nonnull Supplier theSupplier); +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java index c8fba47fc8a..42b1d77be9a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java @@ -19,12 +19,7 @@ */ package ca.uhn.fhir.rest.server.provider; -import jakarta.annotation.Nonnull; - -import java.util.function.Supplier; - -public interface IResourceProviderFactoryObserver { - void update(@Nonnull Supplier theSupplier); - - void remove(@Nonnull Supplier theSupplier); -} +/** + * See {@link ObservableSupplierSet} + */ +public interface IResourceProviderFactoryObserver extends IObservableSupplierSetObserver {} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSet.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSet.java new file mode 100644 index 00000000000..f20eddb181b --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSet.java @@ -0,0 +1,123 @@ +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.rest.server.provider; + +import jakarta.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +/** + * This is a generic implementation of the Observer Design Pattern. + * We use this to pass sets of beans from exporting Spring application contexts to importing Spring application contexts. We defer + * resolving the observed beans via a Supplier to give the exporting context a chance to initialize the beans before they are used. + * @param the class of the Observer + *

+ * A typical usage pattern would be: + *

    + *
  1. Create {@link ObservableSupplierSet} in exporter context.
  2. + *
  3. Add all the suppliers in the exporter context.
  4. + *
  5. Attach the importer to the {@link ObservableSupplierSet}
  6. + *
  7. Importer calls {@link ObservableSupplierSet#getSupplierResults} and processes all the beans
  8. + *
  9. Some other service beans may add more suppliers later as a part of their initialization and the observer handlers will process them accordingly
  10. + *
  11. Those other service beans should call {@link ObservableSupplierSet#removeSupplier(Supplier)} in a @PreDestroy method so they are properly cleaned up if those services are shut down or restarted
  12. + *
+ * + */ +public class ObservableSupplierSet { + private static final Logger ourLog = LoggerFactory.getLogger(ObservableSupplierSet.class); + + private final Set myObservers = Collections.synchronizedSet(new HashSet<>()); + + private final Set> mySuppliers = new LinkedHashSet<>(); + + /** Add a supplier and notify all observers + * + * @param theSupplier supplies the object to be observed + */ + public void addSupplier(@Nonnull Supplier theSupplier) { + if (mySuppliers.add(theSupplier)) { + myObservers.forEach(observer -> observer.update(theSupplier)); + } + } + + /** Remove a supplier and notify all observers. CAUTION, you might think that this code would work, but it does not: + * + * observableSupplierSet.addSupplier(() -> myBean); + * ... + * observableSupplierSet.removeSupplier(() -> myBean); + * + * the removeSupplier in this example would fail because it is a different lambda instance from the first. Instead, + * you need to store the supplier between the add and remove: + * + * mySupplier = () -> myBean; + * observableSupplierSet.addSupplier(mySupplier); + * ... + * observableSupplierSet.removeSupplier(mySupplier); + * + * + * @param theSupplier the supplier to be removed + */ + public void removeSupplier(@Nonnull Supplier theSupplier) { + if (mySuppliers.remove(theSupplier)) { + myObservers.forEach(observer -> observer.remove(theSupplier)); + } else { + ourLog.warn("Failed to remove supplier", new RuntimeException()); + } + } + + /** + * Attach an observer to this observableSupplierSet. This observer will be notified every time a supplier is added or removed. + * @param theObserver the observer to be notified + */ + public void attach(T theObserver) { + myObservers.add(theObserver); + } + + /** + * Detach an observer from this observableSupplierSet, so it is no longer notified when suppliers are added and removed. + * @param theObserver the observer to be removed + */ + public void detach(T theObserver) { + myObservers.remove(theObserver); + } + + /** + * + * @return a list of get() being called on all suppliers. + */ + protected List getSupplierResults() { + List retVal = new ArrayList<>(); + for (Supplier next : mySuppliers) { + Object nextRp = next.get(); + if (nextRp != null) { + retVal.add(nextRp); + } + } + return retVal; + } +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java index 809418c1241..dce86b45e30 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java @@ -19,45 +19,16 @@ */ package ca.uhn.fhir.rest.server.provider; -import jakarta.annotation.Nonnull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.function.Supplier; -public class ResourceProviderFactory { - private Set myObservers = Collections.synchronizedSet(new HashSet<>()); - private List> mySuppliers = new ArrayList<>(); - - public void addSupplier(@Nonnull Supplier theSupplier) { - mySuppliers.add(theSupplier); - myObservers.forEach(observer -> observer.update(theSupplier)); - } - - public void removeSupplier(@Nonnull Supplier theSupplier) { - mySuppliers.remove(theSupplier); - myObservers.forEach(observer -> observer.remove(theSupplier)); - } +/** + * This Factory stores FHIR Resource Provider instance suppliers that will be registered on a FHIR Endpoint later. + * See {@link ObservableSupplierSet} + */ +public class ResourceProviderFactory extends ObservableSupplierSet { + public ResourceProviderFactory() {} public List createProviders() { - List retVal = new ArrayList<>(); - for (Supplier next : mySuppliers) { - Object nextRp = next.get(); - if (nextRp != null) { - retVal.add(nextRp); - } - } - return retVal; - } - - public void attach(IResourceProviderFactoryObserver theObserver) { - myObservers.add(theObserver); - } - - public void detach(IResourceProviderFactoryObserver theObserver) { - myObservers.remove(theObserver); + return super.getSupplierResults(); } } diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index e51308bd2a7..41d0b9e5491 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml index 32680b37acb..47a646b0196 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index da34ff1770e..1bb5308eec9 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml index 3e09e7c0160..615bcaede67 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml @@ -7,7 +7,7 @@ hapi-fhir ca.uhn.hapi.fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 3a7bdba7d54..4e9f671c79c 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 93b126309c1..5514a27054c 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 7a250b4b4c5..638c3933040 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index a99bea8806d..d3e81bb97d3 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index e3b0cf47a83..6ec3497c477 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index f45a5e76eba..133fa52cfd8 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index d554053161d..e23fe9696d2 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 8b56e169941..25eee371362 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 0827041f3d5..e10a99c7db0 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProviderTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProviderTest.java index c7b55ff0b53..5d0b04056dd 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProviderTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProviderTest.java @@ -24,9 +24,9 @@ public class SchemaInitializationProviderTest { -- comment in a weird spot references CDR_XACT_LOG; - -- we can't use convering index until the autovacuum runs for those rows, which kills index performance + -- we can't use covering index until the autovacuum runs for those rows, which kills index performance ALTER TABLE hfj_resource SET (autovacuum_vacuum_scale_factor = 0.01); - ALTER TABLE hfj_forced_id SET (autovacuum_vacuum_scale_factor = 0.01); + ALTER TABLE hfj_spidx_token SET (autovacuum_vacuum_scale_factor = 0.01); """; List listToPopulate = new ArrayList<>(); svc.parseSqlFileIntoIndividualStatements(DriverTypeEnum.POSTGRES_9_4, listToPopulate, input); @@ -35,7 +35,7 @@ public class SchemaInitializationProviderTest { "create sequence foo", "alter table if exists CDR_XACT_LOG_STEP add constraint FK_XACTLOGSTEP_XACTLOG foreign key (LOG_PID) references CDR_XACT_LOG", "ALTER TABLE hfj_resource SET (autovacuum_vacuum_scale_factor = 0.01)", - "ALTER TABLE hfj_forced_id SET (autovacuum_vacuum_scale_factor = 0.01)" + "ALTER TABLE hfj_spidx_token SET (autovacuum_vacuum_scale_factor = 0.01)" )); } diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index bb38f3ccee7..814d926048b 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java index 8882abc5c2e..05f5a95dbcc 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java @@ -37,7 +37,6 @@ import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportResponseJson; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -77,7 +76,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -293,11 +291,15 @@ public class BulkDataExportProvider { */ private void validateTargetsExists( RequestDetails theRequestDetails, String theTargetResourceName, Iterable theIdParams) { - RequestPartitionId partitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForRead( - theRequestDetails, theTargetResourceName, theIdParams.iterator().next()); - SystemRequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId(partitionId); - for (IIdType nextId : theIdParams) { - myDaoRegistry.getResourceDao(theTargetResourceName).read(nextId, requestDetails); + if (theIdParams.iterator().hasNext()) { + RequestPartitionId partitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForRead( + theRequestDetails, + theTargetResourceName, + theIdParams.iterator().next()); + SystemRequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId(partitionId); + for (IIdType nextId : theIdParams) { + myDaoRegistry.getResourceDao(theTargetResourceName).read(nextId, requestDetails); + } } } @@ -364,26 +366,18 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { - validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); - if (thePatient != null) { - validateTargetsExists( - theRequestDetails, - "Patient", - thePatient.stream().map(s -> new IdDt(s.getValue())).collect(Collectors.toList())); - } + List> patientIds = thePatient != null ? thePatient : new ArrayList<>(); - BulkExportJobParameters BulkExportJobParameters = buildPatientBulkExportOptions( + doPatientExport( + theRequestDetails, theOutputFormat, theType, theSince, - theTypeFilter, theExportIdentifier, - thePatient, - theTypePostFetchFilterUrl); - validateResourceTypesAllContainPatientSearchParams(BulkExportJobParameters.getResourceTypes()); - - startJob(theRequestDetails, BulkExportJobParameters); + theTypeFilter, + theTypePostFetchFilterUrl, + patientIds); } /** @@ -417,9 +411,34 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { + + // call the type-level export to ensure spec compliance + patientExport( + theOutputFormat, + theType, + theSince, + theTypeFilter, + theTypePostFetchFilterUrl, + List.of(theIdParam), + theExportIdentifier, + theRequestDetails); + } + + private void doPatientExport( + ServletRequestDetails theRequestDetails, + IPrimitiveType theOutputFormat, + IPrimitiveType theType, + IPrimitiveType theSince, + IPrimitiveType theExportIdentifier, + List> theTypeFilter, + List> theTypePostFetchFilterUrl, + List> thePatientIds) { validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); - validateTargetsExists(theRequestDetails, "Patient", List.of(theIdParam)); + validateTargetsExists( + theRequestDetails, + "Patient", + thePatientIds.stream().map(c -> new IdType(c.getValue())).collect(Collectors.toList())); BulkExportJobParameters BulkExportJobParameters = buildPatientBulkExportOptions( theOutputFormat, @@ -427,7 +446,7 @@ public class BulkDataExportProvider { theSince, theTypeFilter, theExportIdentifier, - theIdParam, + thePatientIds, theTypePostFetchFilterUrl); validateResourceTypesAllContainPatientSearchParams(BulkExportJobParameters.getResourceTypes()); @@ -670,31 +689,6 @@ public class BulkDataExportProvider { return BulkExportJobParameters; } - private BulkExportJobParameters buildPatientBulkExportOptions( - IPrimitiveType theOutputFormat, - IPrimitiveType theType, - IPrimitiveType theSince, - List> theTypeFilter, - IPrimitiveType theExportIdentifier, - IIdType thePatientId, - List> theTypePostFetchFilterUrl) { - IPrimitiveType type = theType; - if (type == null) { - // set type to all patient compartment resources if it is null - type = new StringDt(String.join(",", getPatientCompartmentResources())); - } - BulkExportJobParameters BulkExportJobParameters = buildBulkExportJobParameters( - theOutputFormat, - type, - theSince, - theTypeFilter, - theExportIdentifier, - ExportStyle.PATIENT, - theTypePostFetchFilterUrl); - BulkExportJobParameters.setPatientIds(Collections.singleton(thePatientId.getValue())); - return BulkExportJobParameters; - } - private BulkExportJobParameters buildBulkExportJobParameters( IPrimitiveType theOutputFormat, IPrimitiveType theType, diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index ce8ac389e76..59ba95cfeb2 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java index 65f8388b4f4..53608062525 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java @@ -64,7 +64,7 @@ import static org.mockito.Mockito.verify; * These tests are abstract, and do not depend on JPA. * Test setups should use the public batch2 api to create scenarios. */ -public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMaintenanceActions, IInProgressActionsTests, IInstanceStateTransitions, IWorkChunkCommon, WorkChunkTestConstants { +public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMaintenanceActions, IInProgressActionsTests, IInstanceStateTransitions, ITestFixture, IWorkChunkCommon, WorkChunkTestConstants { private static final Logger ourLog = LoggerFactory.getLogger(AbstractIJobPersistenceSpecificationTest.class); @@ -107,6 +107,11 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa return builder.build(); } + @Override + public ITestFixture getTestManager() { + return this; + } + @AfterEach public void after() { myJobDefinitionRegistry.removeJobDefinition(JOB_DEFINITION_ID, JOB_DEF_VER); @@ -123,7 +128,7 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa class WorkChunkStorage implements IWorkChunkStorageTests { @Override - public IWorkChunkCommon getTestManager() { + public ITestFixture getTestManager() { return AbstractIJobPersistenceSpecificationTest.this; } @@ -131,7 +136,7 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa class StateTransitions implements IWorkChunkStateTransitions { @Override - public IWorkChunkCommon getTestManager() { + public ITestFixture getTestManager() { return AbstractIJobPersistenceSpecificationTest.this; } @@ -139,18 +144,13 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa class ErrorActions implements IWorkChunkErrorActionsTests { @Override - public IWorkChunkCommon getTestManager() { + public ITestFixture getTestManager() { return AbstractIJobPersistenceSpecificationTest.this; } } } } - @Override - public IWorkChunkCommon getTestManager() { - return this; - } - @Nonnull public JobInstance createInstance(JobDefinition theJobDefinition) { JobDefinition jobDefinition = theJobDefinition == null ? withJobDefinition(false) @@ -183,6 +183,9 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa return runInTransaction(() -> mySvc.fetchInstance(theInstanceId).orElseThrow()); } + @Override + public abstract void runMaintenancePass(); + public TransactionTemplate newTxTemplate() { TransactionTemplate retVal = new TransactionTemplate(getTxManager()); retVal.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); @@ -259,6 +262,7 @@ public abstract class AbstractIJobPersistenceSpecificationTest implements IJobMa .sendWorkChannelMessage(notificationCaptor.capture()); } + @Override public void createChunksInStates(JobMaintenanceStateInformation theJobMaintenanceStateInformation) { theJobMaintenanceStateInformation.initialize(mySvc); } diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java index c1667424a7a..63af19404b2 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent; @@ -12,13 +31,13 @@ public interface IInProgressActionsTests extends IWorkChunkCommon, WorkChunkTest @Test default void processingOk_inProgressToSuccess_clearsDataSavesRecordCount() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); // execution ok - getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 0)); + getTestManager().getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 0)); // verify the db was updated - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus()); assertNull(workChunkEntity.getData()); assertEquals(3, workChunkEntity.getRecordsProcessed()); @@ -28,13 +47,13 @@ public interface IInProgressActionsTests extends IWorkChunkCommon, WorkChunkTest @Test default void processingRetryableError_inProgressToError_bumpsCountRecordsMessage() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); // execution had a retryable error - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_A)); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_A)); // verify the db was updated - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus()); assertEquals(ERROR_MESSAGE_A, workChunkEntity.getErrorMessage()); assertEquals(1, workChunkEntity.getErrorCount()); @@ -42,13 +61,13 @@ public interface IInProgressActionsTests extends IWorkChunkCommon, WorkChunkTest @Test default void processingFailure_inProgressToFailed() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); // execution had a failure - getSvc().onWorkChunkFailed(myChunkId, "some error"); + getTestManager().getSvc().onWorkChunkFailed(myChunkId, "some error"); // verify the db was updated - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.FAILED, workChunkEntity.getStatus()); assertEquals("some error", workChunkEntity.getErrorMessage()); } diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java index 95ce1e060e4..89e99aaa125 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; import ca.uhn.fhir.batch2.api.IJobPersistence; @@ -20,6 +39,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -30,12 +50,12 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe @Test default void createInstance_createsInQueuedWithChunkInReady() { // given - JobDefinition jd = withJobDefinition(false); + JobDefinition jd = getTestManager().withJobDefinition(false); // when IJobPersistence.CreateResult createResult = - newTxTemplate().execute(status-> - getSvc().onCreateWithFirstChunk(jd, "{}")); + getTestManager().newTxTemplate().execute(status-> + getTestManager().getSvc().onCreateWithFirstChunk(jd, "{}")); // then ourLog.info("job and chunk created {}", createResult); @@ -43,11 +63,11 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe assertThat(createResult.jobInstanceId, not(emptyString())); assertThat(createResult.workChunkId, not(emptyString())); - JobInstance jobInstance = freshFetchJobInstance(createResult.jobInstanceId); + JobInstance jobInstance = getTestManager().freshFetchJobInstance(createResult.jobInstanceId); assertThat(jobInstance.getStatus(), equalTo(StatusEnum.QUEUED)); assertThat(jobInstance.getParameters(), equalTo("{}")); - WorkChunk firstChunk = freshFetchWorkChunk(createResult.workChunkId); + WorkChunk firstChunk = getTestManager().freshFetchWorkChunk(createResult.workChunkId); assertThat(firstChunk.getStatus(), equalTo(WorkChunkStatusEnum.READY)); assertNull(firstChunk.getData(), "First chunk data is null - only uses parameters"); } @@ -55,16 +75,16 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe @Test default void testCreateInstance_firstChunkDequeued_movesToInProgress() { // given - JobDefinition jd = withJobDefinition(false); - IJobPersistence.CreateResult createResult = newTxTemplate().execute(status-> - getSvc().onCreateWithFirstChunk(jd, "{}")); + JobDefinition jd = getTestManager().withJobDefinition(false); + IJobPersistence.CreateResult createResult = getTestManager().newTxTemplate().execute(status-> + getTestManager().getSvc().onCreateWithFirstChunk(jd, "{}")); assertNotNull(createResult); // when - newTxTemplate().execute(status -> getSvc().onChunkDequeued(createResult.jobInstanceId)); + getTestManager().newTxTemplate().execute(status -> getTestManager().getSvc().onChunkDequeued(createResult.jobInstanceId)); // then - JobInstance jobInstance = freshFetchJobInstance(createResult.jobInstanceId); + JobInstance jobInstance = getTestManager().freshFetchJobInstance(createResult.jobInstanceId); assertThat(jobInstance.getStatus(), equalTo(StatusEnum.IN_PROGRESS)); } @@ -74,20 +94,20 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe // given JobInstance cancelledInstance = createInstance(); cancelledInstance.setStatus(theState); - String instanceId1 = getSvc().storeNewInstance(cancelledInstance); - getSvc().cancelInstance(instanceId1); + String instanceId1 = getTestManager().getSvc().storeNewInstance(cancelledInstance); + getTestManager().getSvc().cancelInstance(instanceId1); JobInstance normalInstance = createInstance(); normalInstance.setStatus(theState); - String instanceId2 = getSvc().storeNewInstance(normalInstance); + String instanceId2 = getTestManager().getSvc().storeNewInstance(normalInstance); JobDefinitionRegistry jobDefinitionRegistry = new JobDefinitionRegistry(); - jobDefinitionRegistry.addJobDefinitionIfNotRegistered(withJobDefinition(false)); + jobDefinitionRegistry.addJobDefinitionIfNotRegistered(getTestManager().withJobDefinition(false)); // when - runInTransaction(()-> { + getTestManager().runInTransaction(()-> { new JobInstanceProcessor( - getSvc(), + getTestManager().getSvc(), null, instanceId1, new JobChunkProgressAccumulator(), @@ -97,7 +117,7 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe }); // then - JobInstance freshInstance1 = getSvc().fetchInstance(instanceId1).orElseThrow(); + JobInstance freshInstance1 = getTestManager().getSvc().fetchInstance(instanceId1).orElseThrow(); if (theState.isCancellable()) { assertEquals(StatusEnum.CANCELLED, freshInstance1.getStatus(), "cancel request processed"); assertThat(freshInstance1.getErrorMessage(), containsString("Job instance cancelled")); @@ -105,17 +125,7 @@ public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTe assertEquals(theState, freshInstance1.getStatus(), "cancel request ignored - state unchanged"); assertNull(freshInstance1.getErrorMessage(), "no error message"); } - JobInstance freshInstance2 = getSvc().fetchInstance(instanceId2).orElseThrow(); + JobInstance freshInstance2 = getTestManager().getSvc().fetchInstance(instanceId2).orElseThrow(); assertEquals(theState, freshInstance2.getStatus(), "cancel request ignored - cancelled not set"); } - - default JobInstance createInstance() { - JobInstance instance = new JobInstance(); - instance.setJobDefinitionId(JOB_DEFINITION_ID); - instance.setStatus(StatusEnum.QUEUED); - instance.setJobDefinitionVersion(JOB_DEF_VER); - instance.setParameters(CHUNK_DATA); - instance.setReport("TEST"); - return instance; - } } diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IJobMaintenanceActions.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IJobMaintenanceActions.java index 3824e35b81e..1fa09807103 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IJobMaintenanceActions.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IJobMaintenanceActions.java @@ -17,7 +17,7 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC @BeforeEach default void before() { - enableMaintenanceRunner(false); + getTestManager().enableMaintenanceRunner(false); } @Test @@ -30,16 +30,16 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC 2|READY,2|QUEUED """; int numToTransition = 2; - PointcutLatch sendLatch = disableWorkChunkMessageHandler(); + PointcutLatch sendLatch = getTestManager().disableWorkChunkMessageHandler(); sendLatch.setExpectedCount(numToTransition); JobMaintenanceStateInformation result = setupGatedWorkChunkTransitionTest(initialState, true); - createChunksInStates(result); + getTestManager().createChunksInStates(result); // test - runMaintenancePass(); + getTestManager().runMaintenancePass(); // verify - verifyWorkChunkMessageHandlerCalled(sendLatch, numToTransition); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendLatch, numToTransition); verifyWorkChunkFinalStates(result); } @@ -111,18 +111,18 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC default void testGatedStep2NotReady_notAdvance(String theChunkState) throws InterruptedException { // setup int expectedLatchCount = getLatchCountFromState(theChunkState); - PointcutLatch sendingLatch = disableWorkChunkMessageHandler(); + PointcutLatch sendingLatch = getTestManager().disableWorkChunkMessageHandler(); sendingLatch.setExpectedCount(expectedLatchCount); JobMaintenanceStateInformation result = setupGatedWorkChunkTransitionTest(theChunkState, true); - createChunksInStates(result); + getTestManager().createChunksInStates(result); // test - runMaintenancePass(); + getTestManager().runMaintenancePass(); // verify // nothing ever queued -> nothing ever sent to queue - verifyWorkChunkMessageHandlerCalled(sendingLatch, expectedLatchCount); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendingLatch, expectedLatchCount); verifyWorkChunkFinalStates(result); } @@ -166,16 +166,16 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC }) default void testGatedStep2ReadyToAdvance_advanceToStep3(String theChunkState) throws InterruptedException { // setup - PointcutLatch sendingLatch = disableWorkChunkMessageHandler(); + PointcutLatch sendingLatch = getTestManager().disableWorkChunkMessageHandler(); sendingLatch.setExpectedCount(2); JobMaintenanceStateInformation result = setupGatedWorkChunkTransitionTest(theChunkState, true); - createChunksInStates(result); + getTestManager().createChunksInStates(result); // test - runMaintenancePass(); + getTestManager().runMaintenancePass(); // verify - verifyWorkChunkMessageHandlerCalled(sendingLatch, 2); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendingLatch, 2); verifyWorkChunkFinalStates(result); } @@ -194,24 +194,24 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC int expectedTransitions = 2; JobMaintenanceStateInformation result = setupGatedWorkChunkTransitionTest(state, false); - PointcutLatch sendLatch = disableWorkChunkMessageHandler(); + PointcutLatch sendLatch = getTestManager().disableWorkChunkMessageHandler(); sendLatch.setExpectedCount(expectedTransitions); - createChunksInStates(result); + getTestManager().createChunksInStates(result); // TEST run job maintenance - force transition - enableMaintenanceRunner(true); + getTestManager().enableMaintenanceRunner(true); - runMaintenancePass(); + getTestManager().runMaintenancePass(); // verify - verifyWorkChunkMessageHandlerCalled(sendLatch, expectedTransitions); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendLatch, expectedTransitions); verifyWorkChunkFinalStates(result); } private JobMaintenanceStateInformation setupGatedWorkChunkTransitionTest(String theChunkState, boolean theIsGated) { // get the job def and store the instance - JobDefinition definition = withJobDefinition(theIsGated); - String instanceId = createAndStoreJobInstance(definition); + JobDefinition definition = getTestManager().withJobDefinition(theIsGated); + String instanceId = getTestManager().createAndStoreJobInstance(definition); JobMaintenanceStateInformation stateInformation = new JobMaintenanceStateInformation(instanceId, definition, theChunkState); ourLog.info("Starting test case \n {}", theChunkState); @@ -221,6 +221,6 @@ public interface IJobMaintenanceActions extends IWorkChunkCommon, WorkChunkTestC } private void verifyWorkChunkFinalStates(JobMaintenanceStateInformation theStateInformation) { - theStateInformation.verifyFinalStates(getSvc()); + theStateInformation.verifyFinalStates(getTestManager().getSvc()); } } diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java new file mode 100644 index 00000000000..4425b062c6b --- /dev/null +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java @@ -0,0 +1,91 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.batch2.test; + +import ca.uhn.fhir.batch2.api.IJobPersistence; +import ca.uhn.fhir.batch2.model.JobDefinition; +import ca.uhn.fhir.batch2.model.JobInstance; +import ca.uhn.fhir.batch2.model.WorkChunk; +import ca.uhn.hapi.fhir.batch2.test.support.JobMaintenanceStateInformation; +import ca.uhn.hapi.fhir.batch2.test.support.TestJobParameters; +import ca.uhn.test.concurrency.PointcutLatch; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +public interface ITestFixture { + + String createAndStoreJobInstance(JobDefinition theJobDefinition); + + String createAndDequeueWorkChunk(String theJobInstanceId); + + WorkChunk freshFetchWorkChunk(String theChunkId); + + String storeWorkChunk(String theJobDefinitionId, String theTargetStepId, String theInstanceId, int theSequence, String theSerializedData); + + void runInTransaction(Runnable theRunnable); + + void sleepUntilTimeChanges(); + + JobDefinition withJobDefinition(boolean theIsGatedJob); + + TransactionTemplate newTxTemplate(); + + JobInstance freshFetchJobInstance(String theInstanceId); + + void runMaintenancePass(); + + PlatformTransactionManager getTransactionManager(); + + IJobPersistence getSvc(); + + /** + * This assumes a creation of JOB_DEFINITION already + * @param theJobInstanceId + * @return + */ + String createChunk(String theJobInstanceId); + + /** + * Enable/disable the maintenance runner (So it doesn't run on a scheduler) + */ + void enableMaintenanceRunner(boolean theToEnable); + + /** + * Disables the workchunk message handler + * so that we do not actually send messages to the queue; + * useful if mocking state transitions and we don't want to test + * dequeuing. + * Returns a latch that will fire each time a message is sent. + */ + PointcutLatch disableWorkChunkMessageHandler(); + + /** + * + * @param theSendingLatch the latch sent back from the disableWorkChunkMessageHandler method above + * @param theNumberOfTimes the number of invocations to expect + */ + void verifyWorkChunkMessageHandlerCalled(PointcutLatch theSendingLatch, int theNumberOfTimes) throws InterruptedException; + + /** + * Uses the JobMaintenanceStateInformation to setup a test. + * @param theJobMaintenanceStateInformation + */ + void createChunksInStates(JobMaintenanceStateInformation theJobMaintenanceStateInformation); +} diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java index b1e7417635b..d9fabe6d0db 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java @@ -1,15 +1,26 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; -import ca.uhn.fhir.batch2.api.IJobPersistence; -import ca.uhn.fhir.batch2.channel.BatchJobSender; -import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.batch2.model.JobInstance; -import ca.uhn.fhir.batch2.model.WorkChunk; -import ca.uhn.hapi.fhir.batch2.test.support.JobMaintenanceStateInformation; -import ca.uhn.hapi.fhir.batch2.test.support.TestJobParameters; -import ca.uhn.test.concurrency.PointcutLatch; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.TransactionTemplate; +import ca.uhn.fhir.batch2.model.StatusEnum; public interface IWorkChunkCommon extends WorkChunkTestConstants { @@ -17,106 +28,15 @@ public interface IWorkChunkCommon extends WorkChunkTestConstants { * Returns the concrete class that is implementing this stuff. * Used primarily for structure */ - IWorkChunkCommon getTestManager(); - - default String createAndStoreJobInstance(JobDefinition theJobDefinition) { - return getTestManager().createAndStoreJobInstance(theJobDefinition); - } - - default String createAndDequeueWorkChunk(String theJobInstanceId) { - return getTestManager().createAndDequeueWorkChunk(theJobInstanceId); - } - - default WorkChunk freshFetchWorkChunk(String theChunkId) { - return getTestManager().freshFetchWorkChunk(theChunkId); - } + ITestFixture getTestManager(); default JobInstance createInstance() { - return getTestManager().createInstance(); - } - - default String storeWorkChunk(String theJobDefinitionId, String theTargetStepId, String theInstanceId, int theSequence, String theSerializedData, boolean theGatedExecution) { - return getTestManager().storeWorkChunk(theJobDefinitionId, theTargetStepId, theInstanceId, theSequence, theSerializedData, theGatedExecution); - } - - default void runInTransaction(Runnable theRunnable) { - getTestManager().runInTransaction(theRunnable); - } - - default void sleepUntilTimeChanges() { - getTestManager().sleepUntilTimeChanges(); - } - - default JobDefinition withJobDefinition(boolean theIsGatedJob) { - return getTestManager().withJobDefinition(theIsGatedJob); - } - - default TransactionTemplate newTxTemplate() { - return getTestManager().newTxTemplate(); - } - - default JobInstance freshFetchJobInstance(String theInstanceId) { - return getTestManager().freshFetchJobInstance(theInstanceId); - } - - default void runMaintenancePass() { - getTestManager().runMaintenancePass(); - } - - default PlatformTransactionManager getTransactionManager() { - return getTestManager().getTransactionManager(); - } - - default IJobPersistence getSvc() { - return getTestManager().getSvc(); - } - - /** - * This assumes a creation of JOB_DEFINITION already - * @param theJobInstanceId - * @return - */ - default String createChunk(String theJobInstanceId) { - return getTestManager().createChunk(theJobInstanceId); - } - - default String createChunk(String theJobInstanceId, boolean theGatedExecution) { - return getTestManager().createChunk(theJobInstanceId, theGatedExecution); - } - - /** - * Enable/disable the maintenance runner (So it doesn't run on a scheduler) - */ - default void enableMaintenanceRunner(boolean theToEnable) { - getTestManager().enableMaintenanceRunner(theToEnable); - } - - /** - * Uses the JobMaintenanceState information and the format: - * "step_number|initialstate,step_number|finalstate" to construct - * an initial state for a test scenario - */ - default void createChunksInStates(JobMaintenanceStateInformation theInitialState) { - getTestManager().createChunksInStates(theInitialState); - } - - /** - * Disables the workchunk message handler - * so that we do not actually send messages to the queue; - * useful if mocking state transitions and we don't want to test - * dequeuing. - * Returns a latch that will fire each time a message is sent. - */ - default PointcutLatch disableWorkChunkMessageHandler() { - return getTestManager().disableWorkChunkMessageHandler(); - } - - /** - * - * @param theSendingLatch the latch sent back from the disableWorkChunkMessageHandler method above - * @param theNumberOfTimes the number of invocations to expect - */ - default void verifyWorkChunkMessageHandlerCalled(PointcutLatch theSendingLatch, int theNumberOfTimes) throws InterruptedException { - getTestManager().verifyWorkChunkMessageHandlerCalled(theSendingLatch, theNumberOfTimes); + JobInstance instance = new JobInstance(); + instance.setJobDefinitionId(JOB_DEFINITION_ID); + instance.setStatus(StatusEnum.QUEUED); + instance.setJobDefinitionVersion(JOB_DEF_VER); + instance.setParameters(CHUNK_DATA); + instance.setReport("TEST"); + return instance; } } diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java index 3f8286d1c52..adabfa120c7 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; import ca.uhn.fhir.batch2.model.WorkChunk; @@ -18,18 +37,18 @@ public interface IWorkChunkErrorActionsTests extends IWorkChunkCommon, WorkChunk */ @Test default void errorRetry_errorToInProgress() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); // when consumer restarts chunk - WorkChunk chunk = getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new); + WorkChunk chunk = getTestManager().getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new); // then assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus()); // verify the db state, error message, and error count - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.IN_PROGRESS, workChunkEntity.getStatus()); assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Original error message kept"); assertEquals(1, workChunkEntity.getErrorCount(), "error count kept"); @@ -37,19 +56,19 @@ public interface IWorkChunkErrorActionsTests extends IWorkChunkCommon, WorkChunk @Test default void errorRetry_repeatError_increasesErrorCount() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); // setup - the consumer is re-trying, and marks it IN_PROGRESS - getSvc().onWorkChunkDequeue(myChunkId); + getTestManager().getSvc().onWorkChunkDequeue(myChunkId); // when another error happens - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); // verify the state, new message, and error count - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus()); assertEquals(ERROR_MESSAGE_B, workChunkEntity.getErrorMessage(), "new error message"); assertEquals(2, workChunkEntity.getErrorCount(), "error count inc"); @@ -57,18 +76,18 @@ public interface IWorkChunkErrorActionsTests extends IWorkChunkCommon, WorkChunk @Test default void errorThenRetryAndComplete_addsErrorCounts() { - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); // setup - the consumer is re-trying, and marks it IN_PROGRESS - getSvc().onWorkChunkDequeue(myChunkId); + getTestManager().getSvc().onWorkChunkDequeue(myChunkId); // then it completes ok. - getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 1)); + getTestManager().getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 1)); // verify the state, new message, and error count - var workChunkEntity = freshFetchWorkChunk(myChunkId); + var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus()); assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Error message kept."); assertEquals(2, workChunkEntity.getErrorCount(), "error combined with earlier error"); @@ -77,28 +96,28 @@ public interface IWorkChunkErrorActionsTests extends IWorkChunkCommon, WorkChunk @Test default void errorRetry_maxErrors_movesToFailed() { // we start with 1 error already - String jobId = createAndStoreJobInstance(null); - String myChunkId = createAndDequeueWorkChunk(jobId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); + String jobId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE)); // 2nd try - getSvc().onWorkChunkDequeue(myChunkId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); - var chunk = freshFetchWorkChunk(myChunkId); + getTestManager().getSvc().onWorkChunkDequeue(myChunkId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); + var chunk = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus()); assertEquals(2, chunk.getErrorCount()); // 3rd try - getSvc().onWorkChunkDequeue(myChunkId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); - chunk = freshFetchWorkChunk(myChunkId); + getTestManager().getSvc().onWorkChunkDequeue(myChunkId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B)); + chunk = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus()); assertEquals(3, chunk.getErrorCount()); // 4th try - getSvc().onWorkChunkDequeue(myChunkId); - getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_C)); - chunk = freshFetchWorkChunk(myChunkId); + getTestManager().getSvc().onWorkChunkDequeue(myChunkId); + getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_C)); + chunk = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.FAILED, chunk.getStatus()); assertEquals(4, chunk.getErrorCount()); assertThat("Error message contains last error", chunk.getErrorMessage(), containsString(ERROR_MESSAGE_C)); diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java index eb279780b58..e826b2af6bd 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java @@ -1,5 +1,27 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; +import ca.uhn.fhir.batch2.model.WorkChunk; +import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; + import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; @@ -23,10 +45,10 @@ public interface IWorkChunkStateTransitions extends IWorkChunkCommon, WorkChunkT "true, GATE_WAITING" }) default void chunkCreation_isInExpectedStatus(boolean theGatedExecution, WorkChunkStatusEnum expectedStatus) { - String jobInstanceId = createAndStoreJobInstance(null); - String myChunkId = createChunk(jobInstanceId, theGatedExecution); + String jobInstanceId = getTestManager().createAndStoreJobInstance(null); + String myChunkId = getTestManager().createChunk(jobInstanceId, theGatedExecution); - WorkChunk fetchedWorkChunk = freshFetchWorkChunk(myChunkId); + WorkChunk fetchedWorkChunk = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(expectedStatus, fetchedWorkChunk.getStatus(), "New chunks are " + expectedStatus); } @@ -36,30 +58,30 @@ public interface IWorkChunkStateTransitions extends IWorkChunkCommon, WorkChunkT "true" }) default void chunkReceived_queuedToInProgress(boolean theGatedExecution) throws InterruptedException { - PointcutLatch sendLatch = disableWorkChunkMessageHandler(); + PointcutLatch sendLatch = getTestManager().disableWorkChunkMessageHandler(); sendLatch.setExpectedCount(1); - String jobInstanceId = createAndStoreJobInstance(withJobDefinition(theGatedExecution)); - String myChunkId = createChunk(jobInstanceId, theGatedExecution); + String jobInstanceId = getTestManager().createAndStoreJobInstance(withJobDefinition(theGatedExecution)); + String myChunkId = getTestManager().createChunk(jobInstanceId, theGatedExecution); - runMaintenancePass(); + getTestManager().runMaintenancePass(); // the worker has received the chunk, and marks it started. - WorkChunk chunk = getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new); + WorkChunk chunk = getTestManager().getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new); assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus()); assertEquals(CHUNK_DATA, chunk.getData()); // verify the db was updated too - WorkChunk fetchedWorkChunk = freshFetchWorkChunk(myChunkId); + WorkChunk fetchedWorkChunk = getTestManager().freshFetchWorkChunk(myChunkId); assertEquals(WorkChunkStatusEnum.IN_PROGRESS, fetchedWorkChunk.getStatus()); - verifyWorkChunkMessageHandlerCalled(sendLatch, 1); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendLatch, 1); } @Test default void enqueueWorkChunkForProcessing_enqueuesOnlyREADYChunks() throws InterruptedException { // setup - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); StringBuilder sb = new StringBuilder(); // first step is always complete @@ -73,20 +95,20 @@ public interface IWorkChunkStateTransitions extends IWorkChunkCommon, WorkChunkT .append(status.name()); } String state = sb.toString(); - JobDefinition jobDef = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDef); + JobDefinition jobDef = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDef); JobMaintenanceStateInformation stateInformation = new JobMaintenanceStateInformation( instanceId, jobDef, state ); - createChunksInStates(stateInformation); + getTestManager().createChunksInStates(stateInformation); // test PointcutLatch latch = new PointcutLatch(new Exception().getStackTrace()[0].getMethodName()); latch.setExpectedCount(stateInformation.getInitialWorkChunks().size()); for (WorkChunk chunk : stateInformation.getInitialWorkChunks()) { - getSvc().enqueueWorkChunkForProcessing(chunk.getId(), updated -> { + getTestManager().getSvc().enqueueWorkChunkForProcessing(chunk.getId(), updated -> { // should not update non-ready chunks ourLog.info("Enqueuing chunk with state {}; updated {}", chunk.getStatus().name(), updated); int expected = chunk.getStatus() == WorkChunkStatusEnum.READY ? 1 : 0; diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java index 602708fb189..48bf7264f35 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java @@ -1,5 +1,30 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; +import ca.uhn.fhir.batch2.model.JobInstance; +import ca.uhn.fhir.batch2.model.WorkChunk; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; + import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.WorkChunk; @@ -28,12 +53,12 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC @Test default void testStoreAndFetchWorkChunk_NoData() { JobInstance instance = createInstance(); - String instanceId = getSvc().storeNewInstance(instance); + String instanceId = getTestManager().getSvc().storeNewInstance(instance); - String id = storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, null, false); + String id = getTestManager().storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, null, false); - runInTransaction(() -> { - WorkChunk chunk = freshFetchWorkChunk(id); + getTestManager().runInTransaction(() -> { + WorkChunk chunk = getTestManager().freshFetchWorkChunk(id); assertNull(chunk.getData()); }); } @@ -45,50 +70,50 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC }) default void testWorkChunkCreate_inExpectedStatus(boolean theGatedExecution, WorkChunkStatusEnum expectedStatus) { JobInstance instance = createInstance(); - String instanceId = getSvc().storeNewInstance(instance); + String instanceId = getTestManager().getSvc().storeNewInstance(instance); - enableMaintenanceRunner(false); + getTestManager().enableMaintenanceRunner(false); - String id = storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, CHUNK_DATA, theGatedExecution); + String id = getTestManager().storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, CHUNK_DATA, theGatedExecution); assertNotNull(id); - runInTransaction(() -> assertEquals(expectedStatus, freshFetchWorkChunk(id).getStatus())); + getTestManager().runInTransaction(() -> assertEquals(expectedStatus, getTestManager().freshFetchWorkChunk(id).getStatus())); } @Test default void testNonGatedWorkChunkInReady_IsQueuedDuringMaintenance() throws InterruptedException { // setup int expectedCalls = 1; - enableMaintenanceRunner(false); - PointcutLatch sendingLatch = disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); + PointcutLatch sendingLatch = getTestManager().disableWorkChunkMessageHandler(); sendingLatch.setExpectedCount(expectedCalls); String state = "1|READY,1|QUEUED"; - JobDefinition jobDefinition = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDefinition); + JobDefinition jobDefinition = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDefinition); JobMaintenanceStateInformation stateInformation = new JobMaintenanceStateInformation(instanceId, jobDefinition, state); - createChunksInStates(stateInformation); + getTestManager().createChunksInStates(stateInformation); String id = stateInformation.getInitialWorkChunks().stream().findFirst().orElseThrow().getId(); // verify created in ready - runInTransaction(() -> assertEquals(WorkChunkStatusEnum.READY, freshFetchWorkChunk(id).getStatus())); + getTestManager().runInTransaction(() -> assertEquals(WorkChunkStatusEnum.READY, getTestManager().freshFetchWorkChunk(id).getStatus())); // test - runMaintenancePass(); + getTestManager().runMaintenancePass(); // verify it's in QUEUED now - stateInformation.verifyFinalStates(getSvc()); - verifyWorkChunkMessageHandlerCalled(sendingLatch, expectedCalls); + stateInformation.verifyFinalStates(getTestManager().getSvc()); + getTestManager().verifyWorkChunkMessageHandlerCalled(sendingLatch, expectedCalls); } @Test default void testStoreAndFetchWorkChunk_WithData() { // setup - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); - JobDefinition jobDefinition = withJobDefinition(false); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); + JobDefinition jobDefinition = getTestManager().withJobDefinition(false); JobInstance instance = createInstance(); - String instanceId = getSvc().storeNewInstance(instance); + String instanceId = getTestManager().getSvc().storeNewInstance(instance); // we're not transitioning this state; we're just checking storage of data JobMaintenanceStateInformation info = new JobMaintenanceStateInformation(instanceId, jobDefinition, "1|QUEUED"); @@ -96,14 +121,14 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC chunk.setData(CHUNK_DATA); }); - createChunksInStates(info); + getTestManager().createChunksInStates(info); String id = info.getInitialWorkChunks().stream().findFirst().orElseThrow().getId(); // verify created in QUEUED - runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, freshFetchWorkChunk(id).getStatus())); + getTestManager().runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, getTestManager().freshFetchWorkChunk(id).getStatus())); // test; manually dequeue chunk - WorkChunk chunk = getSvc().onWorkChunkDequeue(id).orElseThrow(IllegalArgumentException::new); + WorkChunk chunk = getTestManager().getSvc().onWorkChunkDequeue(id).orElseThrow(IllegalArgumentException::new); // verify assertEquals(36, chunk.getInstanceId().length()); @@ -112,33 +137,33 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus()); assertEquals(CHUNK_DATA, chunk.getData()); - runInTransaction(() -> assertEquals(WorkChunkStatusEnum.IN_PROGRESS, freshFetchWorkChunk(id).getStatus())); + getTestManager().runInTransaction(() -> assertEquals(WorkChunkStatusEnum.IN_PROGRESS, getTestManager().freshFetchWorkChunk(id).getStatus())); } @Test default void testMarkChunkAsCompleted_Success() { // setup String state = "2|IN_PROGRESS,2|COMPLETED"; - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); - JobDefinition jobDefinition = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDefinition); + JobDefinition jobDefinition = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDefinition); JobMaintenanceStateInformation info = new JobMaintenanceStateInformation(instanceId, jobDefinition, state); info.addWorkChunkModifier(chunk -> { chunk.setCreateTime(new Date()); chunk.setData(CHUNK_DATA); }); - createChunksInStates(info); + getTestManager().createChunksInStates(info); String chunkId = info.getInitialWorkChunks().stream().findFirst().orElseThrow().getId(); // run test - runInTransaction(() -> getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(chunkId, 50, 0))); + getTestManager().runInTransaction(() -> getTestManager().getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(chunkId, 50, 0))); // verify - info.verifyFinalStates(getSvc()); - WorkChunk entity = freshFetchWorkChunk(chunkId); + info.verifyFinalStates(getTestManager().getSvc()); + WorkChunk entity = getTestManager().freshFetchWorkChunk(chunkId); assertEquals(WorkChunkStatusEnum.COMPLETED, entity.getStatus()); assertEquals(50, entity.getRecordsProcessed()); assertNotNull(entity.getCreateTime()); @@ -149,21 +174,21 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC default void testMarkChunkAsCompleted_Error() { // setup String state = "1|IN_PROGRESS,1|ERRORED"; - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); - JobDefinition jobDef = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDef); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); + JobDefinition jobDef = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDef); JobMaintenanceStateInformation info = new JobMaintenanceStateInformation( instanceId, jobDef, state ); - createChunksInStates(info); + getTestManager().createChunksInStates(info); String chunkId = info.getInitialWorkChunks().stream().findFirst().orElseThrow().getId(); // test WorkChunkErrorEvent request = new WorkChunkErrorEvent(chunkId, ERROR_MESSAGE_A); - getSvc().onWorkChunkError(request); - runInTransaction(() -> { - WorkChunk entity = freshFetchWorkChunk(chunkId); + getTestManager().getSvc().onWorkChunkError(request); + getTestManager().runInTransaction(() -> { + WorkChunk entity = getTestManager().freshFetchWorkChunk(chunkId); assertEquals(WorkChunkStatusEnum.ERRORED, entity.getStatus()); assertEquals(ERROR_MESSAGE_A, entity.getErrorMessage()); assertEquals(1, entity.getErrorCount()); @@ -172,46 +197,46 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC // Mark errored again WorkChunkErrorEvent request2 = new WorkChunkErrorEvent(chunkId, "This is an error message 2"); - getSvc().onWorkChunkError(request2); - runInTransaction(() -> { - WorkChunk entity = freshFetchWorkChunk(chunkId); + getTestManager().getSvc().onWorkChunkError(request2); + getTestManager().runInTransaction(() -> { + WorkChunk entity = getTestManager().freshFetchWorkChunk(chunkId); assertEquals(WorkChunkStatusEnum.ERRORED, entity.getStatus()); assertEquals("This is an error message 2", entity.getErrorMessage()); assertEquals(2, entity.getErrorCount()); }); - List chunks = ImmutableList.copyOf(getSvc().fetchAllWorkChunksIterator(instanceId, true)); + List chunks = ImmutableList.copyOf(getTestManager().getSvc().fetchAllWorkChunksIterator(instanceId, true)); assertEquals(1, chunks.size()); assertEquals(2, chunks.get(0).getErrorCount()); - info.verifyFinalStates(getSvc()); + info.verifyFinalStates(getTestManager().getSvc()); } @Test default void testMarkChunkAsCompleted_Fail() { // setup String state = "1|IN_PROGRESS,1|FAILED"; - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); - JobDefinition jobDef = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDef); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); + JobDefinition jobDef = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDef); JobMaintenanceStateInformation info = new JobMaintenanceStateInformation( instanceId, jobDef, state ); - createChunksInStates(info); + getTestManager().createChunksInStates(info); String chunkId = info.getInitialWorkChunks().stream().findFirst().orElseThrow().getId(); // test - getSvc().onWorkChunkFailed(chunkId, "This is an error message"); + getTestManager().getSvc().onWorkChunkFailed(chunkId, "This is an error message"); // verify - runInTransaction(() -> { - WorkChunk entity = freshFetchWorkChunk(chunkId); + getTestManager().runInTransaction(() -> { + WorkChunk entity = getTestManager().freshFetchWorkChunk(chunkId); assertEquals(WorkChunkStatusEnum.FAILED, entity.getStatus()); assertEquals("This is an error message", entity.getErrorMessage()); }); - info.verifyFinalStates(getSvc()); + info.verifyFinalStates(getTestManager().getSvc()); } @Test @@ -223,20 +248,20 @@ public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestC 1|QUEUED,1|COMPLETED 1|IN_PROGRESS,1|COMPLETED """; - disableWorkChunkMessageHandler(); - enableMaintenanceRunner(false); - JobDefinition jobDef = withJobDefinition(false); - String instanceId = createAndStoreJobInstance(jobDef); + getTestManager().disableWorkChunkMessageHandler(); + getTestManager().enableMaintenanceRunner(false); + JobDefinition jobDef = getTestManager().withJobDefinition(false); + String instanceId = getTestManager().createAndStoreJobInstance(jobDef); JobMaintenanceStateInformation info = new JobMaintenanceStateInformation( instanceId, jobDef, state ); - createChunksInStates(info); + getTestManager().createChunksInStates(info); List chunkIds = info.getInitialWorkChunks().stream().map(WorkChunk::getId) .collect(Collectors.toList()); - runInTransaction(() -> getSvc().markWorkChunksWithStatusAndWipeData(instanceId, chunkIds, WorkChunkStatusEnum.COMPLETED, null)); + getTestManager().runInTransaction(() -> getTestManager().getSvc().markWorkChunksWithStatusAndWipeData(instanceId, chunkIds, WorkChunkStatusEnum.COMPLETED, null)); - Iterator reducedChunks = getSvc().fetchAllWorkChunksIterator(instanceId, true); + Iterator reducedChunks = getTestManager().getSvc().fetchAllWorkChunksIterator(instanceId, true); while (reducedChunks.hasNext()) { WorkChunk reducedChunk = reducedChunks.next(); diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java index 9f5df3120ff..8ab0157e52b 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test; public interface WorkChunkTestConstants { diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java index 46331d1c219..91735cd0414 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Batch2 specification tests + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package ca.uhn.hapi.fhir.batch2.test.configs; import ca.uhn.fhir.batch2.channel.BatchJobSender; diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 2d86204e222..7890ad1bfa3 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java index c341fb0e48a..fcdfb1ca9f9 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.batch2.model.ChunkOutcome; import ca.uhn.fhir.batch2.model.JobDefinitionStep; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobWorkCursor; +import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; @@ -137,7 +138,6 @@ public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorS public void reducerPass() { if (myCurrentlyExecuting.tryAcquire()) { try { - String[] instanceIds = myInstanceIdToJobWorkCursor.keySet().toArray(new String[0]); if (instanceIds.length > 0) { String instanceId = instanceIds[0]; @@ -214,6 +214,36 @@ public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorS boolean defaultSuccessValue = true; ReductionStepChunkProcessingResponse response = new ReductionStepChunkProcessingResponse(defaultSuccessValue); + try { + processChunksAndCompleteJob(theJobWorkCursor, step, instance, parameters, reductionStepWorker, response); + } catch (Exception ex) { + ourLog.error("Job completion failed for Job {}", instance.getInstanceId()); + + executeInTransactionWithSynchronization(() -> { + myJobPersistence.updateInstance(instance.getInstanceId(), theInstance -> { + theInstance.setStatus(StatusEnum.FAILED); + return true; + }); + return null; + }); + response.setSuccessful(false); + } + + // if no successful chunks, return false + if (!response.hasSuccessfulChunksIds()) { + response.setSuccessful(false); + } + + return response; + } + + private void processChunksAndCompleteJob( + JobWorkCursor theJobWorkCursor, + JobDefinitionStep step, + JobInstance instance, + PT parameters, + IReductionStepWorker reductionStepWorker, + ReductionStepChunkProcessingResponse response) { try { executeInTransactionWithSynchronization(() -> { try (Stream chunkIterator = @@ -277,13 +307,6 @@ public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorS return null; }); } - - // if no successful chunks, return false - if (!response.hasSuccessfulChunksIds()) { - response.setSuccessful(false); - } - - return response; } private T executeInTransactionWithSynchronization(Callable runnable) { diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index deec953b4d9..30a4b7d9731 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index aaaffd4dfe4..21684ec422d 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java index f65d49ab408..58def7ad3cb 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java @@ -41,8 +41,8 @@ public class MdmSubmitterInterceptorLoader { @Autowired JpaStorageSettings myStorageSettings; - @Autowired - private IMdmStorageInterceptor myIMdmStorageInterceptor; + @Autowired(required = false) + private IMdmStorageInterceptor myMdmStorageInterceptor; @Autowired private MdmSearchExpandingInterceptor myMdmSearchExpandingInterceptorInterceptor; @@ -60,7 +60,9 @@ public class MdmSubmitterInterceptorLoader { throw new ConfigurationException( Msg.code(2421) + "MDM requires Message Subscriptions to be enabled in the Storage Settings"); } - myInterceptorService.registerInterceptor(myIMdmStorageInterceptor); + if (myMdmStorageInterceptor != null) { + myInterceptorService.registerInterceptor(myMdmStorageInterceptor); + } myInterceptorService.registerInterceptor(myMdmSearchExpandingInterceptorInterceptor); ourLog.info("MDM interceptors registered"); } diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 97697fbeceb..ba4f65c01e5 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 96682b6343b..7090925bf5f 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 541a22ad700..146924739b1 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 0dab5394bcd..76dadd587ec 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSetTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSetTest.java new file mode 100644 index 00000000000..d13a161141c --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/provider/ObservableSupplierSetTest.java @@ -0,0 +1,102 @@ +package ca.uhn.fhir.rest.server.provider; + +import ca.uhn.test.util.LogbackCaptureTestExtension; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.*; + +class ObservableSupplierSetTest { + + private final ObservableSupplierSet myObservableSupplierSet = new ObservableSupplierSet<>(); + private final TestObserver myObserver = new TestObserver(); + private final AtomicInteger myCounter = new AtomicInteger(); + + @RegisterExtension + final LogbackCaptureTestExtension myLogger = new LogbackCaptureTestExtension((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ObservableSupplierSet.class)); + + @BeforeEach + public void before() { + myObservableSupplierSet.attach(myObserver); + myObserver.assertCalls(0, 0); + } + + @Test + public void observersNotifiedByAddRemove() { + Supplier supplier = myCounter::incrementAndGet; + myObservableSupplierSet.addSupplier(supplier); + myObserver.assertCalls(1, 0); + assertEquals(0, myCounter.get()); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(1)); + assertEquals(1, myCounter.get()); + myObservableSupplierSet.removeSupplier(supplier); + myObserver.assertCalls(1, 1); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(0)); + } + + @Test + public void testRemoveWrongSupplier() { + myObservableSupplierSet.addSupplier(myCounter::incrementAndGet); + myObserver.assertCalls(1, 0); + assertEquals(0, myCounter.get()); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(1)); + assertEquals(1, myCounter.get()); + + // You might expect this to remove our supplier, but in fact it is a different lambda, so it fails and logs a stack trace + myObservableSupplierSet.removeSupplier(myCounter::incrementAndGet); + myObserver.assertCalls(1, 0); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(1)); + List events = myLogger.filterLoggingEventsWithMessageContaining("Failed to remove supplier"); + assertThat(events, hasSize(1)); + assertEquals(Level.WARN, events.get(0).getLevel()); + } + + @Test + public void testDetach() { + myObservableSupplierSet.addSupplier(myCounter::incrementAndGet); + myObserver.assertCalls(1, 0); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(1)); + + myObservableSupplierSet.addSupplier(myCounter::incrementAndGet); + myObserver.assertCalls(2, 0); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(2)); + + myObservableSupplierSet.detach(myObserver); + + // We now have a third supplier but the observer has been detached, so it was not notified of the third supplier + myObservableSupplierSet.addSupplier(myCounter::incrementAndGet); + myObserver.assertCalls(2, 0); + assertThat(myObservableSupplierSet.getSupplierResults(), hasSize(3)); + } + + private static class TestObserver implements IObservableSupplierSetObserver { + int updated = 0; + int removed = 0; + + @Override + public void update(@NotNull Supplier theSupplier) { + ++updated; + } + + @Override + public void remove(@NotNull Supplier theSupplier) { + ++removed; + } + + public void assertCalls(int theExpectedUpdated, int theExpectedRemoved) { + assertEquals(theExpectedUpdated, updated); + assertEquals(theExpectedRemoved, removed); + } + } +} diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index fc3ad9f5c7f..cfddebcbaae 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 04e09044793..3ec00da7fc7 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index b4150ec9c38..55a7aeba956 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index d852ad84473..73801c099d6 100644 --- a/hapi-fhir-structures-r4b/pom.xml +++ b/hapi-fhir-structures-r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 1c88c4f4869..67862c00146 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index ff040fa535c..eb4c71307da 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackCaptureTestExtension.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackCaptureTestExtension.java index f5f61161ba4..eb58685aad5 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackCaptureTestExtension.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackCaptureTestExtension.java @@ -86,6 +86,14 @@ public class LogbackCaptureTestExtension implements BeforeEachCallback, AfterEac this(theClass.getName()); } + public LogbackCaptureTestExtension(Class theClass, Level theLevel) { + this(theClass.getName(), theLevel); + } + + public LogbackCaptureTestExtension(org.slf4j.Logger theLogger) { + this((Logger) theLogger); + } + /** * Returns a copy to avoid concurrent modification errors. * @return A copy of the log events so far. @@ -154,6 +162,16 @@ public class LogbackCaptureTestExtension implements BeforeEachCallback, AfterEac .collect(Collectors.toList()); } + /** + * Extract the log messages from the logging events. + * @return a copy of the List of log messages + * + */ + @Nonnull + public List getLogMessages() { + return getLogEvents().stream().map(ILoggingEvent::getMessage).collect(Collectors.toList()); + } + // Hamcrest matcher support public static Matcher eventWithLevelAndMessageContains(@Nonnull Level theLevel, @Nonnull String thePartialMessage) { return new LogbackEventMatcher(theLevel, thePartialMessage); diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/StaticLogbackCaptureTestExtension.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/StaticLogbackCaptureTestExtension.java new file mode 100644 index 00000000000..9c8dda60674 --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/StaticLogbackCaptureTestExtension.java @@ -0,0 +1,39 @@ +package ca.uhn.test.util; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.util.List; + +/** + * This is a static wrapper around LogbackCaptureTestExtension for use in IT tests when you need to assert on App + * startup log entries + */ + +public class StaticLogbackCaptureTestExtension implements BeforeAllCallback, AfterAllCallback { + private final LogbackCaptureTestExtension myLogbackCaptureTestExtension; + + public StaticLogbackCaptureTestExtension(LogbackCaptureTestExtension theLogbackCaptureTestExtension) { + myLogbackCaptureTestExtension = theLogbackCaptureTestExtension; + } + + public StaticLogbackCaptureTestExtension() { + myLogbackCaptureTestExtension = new LogbackCaptureTestExtension(); + } + + @Override + public void beforeAll(ExtensionContext theExtensionContext) throws Exception { + myLogbackCaptureTestExtension.beforeEach(theExtensionContext); + } + + @Override + public void afterAll(ExtensionContext theExtensionContext) throws Exception { + myLogbackCaptureTestExtension.afterEach(theExtensionContext); + } + + public List filterLoggingEventsWithMessageEqualTo(String theMessageText) { + return myLogbackCaptureTestExtension.filterLoggingEventsWithMessageEqualTo(theMessageText); + } +} diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 0081f27ff59..02489770f91 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index be3911ce0ac..22142383e08 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index ab34e8d8bcc..27b3b119848 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 522ac2b450c..a33191a05ec 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index bb7ed613bb1..44b6a5f1805 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml index fb7965abff1..d58235a44b1 100644 --- a/hapi-fhir-validation-resources-r4b/pom.xml +++ b/hapi-fhir-validation-resources-r4b/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 11e616c37cd..3bde201665c 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index bd6a099c68c..b1432477175 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 4fb7cf0d88e..368e8aaf6a9 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 807fe45c5b0..421c2a663d9 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -73,11 +73,9 @@ public class ${className}ResourceProvider extends @OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_LIST) StringAndListParam theList, -#if ( $version == 'R5' ) @Description(shortDefinition="The language of the resource") @OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_LANGUAGE) TokenAndListParam theResourceLanguage, -#end @Description(shortDefinition="Search for resources which have the given source value (Resource.meta.source)") @OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_SOURCE) @@ -160,9 +158,8 @@ public class ${className}ResourceProvider extends paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE, theSearchForProfile); paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_SOURCE, theSearchForSource); paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_LIST, theList); -#if ( $version == 'R5' ) paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_LANGUAGE, theResourceLanguage); -#end + paramMap.add("_has", theHas); #foreach ( $param in $searchParams ) paramMap.add("${param.name}", the${param.nameCapitalized}); diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 5f84df6f169..99a2b4d97a8 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index a3004385ff1..2ce4f6d190a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 5f119fcd90b..890e8f28ed4 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 4250751cced..83b846622ad 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 0fd0f7c3774..a4d48372ff5 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.1.7-SNAPSHOT + 7.1.8-SNAPSHOT ../../pom.xml